How to Add Java Beans to Oracle Forms

Java beans in bean areas, pluggable Java components, and FBEAN in Oracle Forms 14.1.2, with properties, events, and JAR deployment.

The Oracle Forms client, whether in a browser, Java Web Start, or the Forms Standalone Launcher, is a Java program that draws the items the server describes. Each kind of item is a Java class: a text item is oracle.forms.ui.VTextField, and a button is VButton. That means you can add Java classes of your own.

This guide covers the three ways to extend the Oracle Forms 14.1.2 client with Java: a Java bean in a bean area, a pluggable Java component that changes a standard item, and the FBEAN package for ordinary JavaBeans. It ends with how to build and deploy the JAR files.

Sample Form for This Guide

The examples and screenshots use the sample forms CH31_BEAN, CH31_PJC, and CH31_FBEAN 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
CH31_BEANforms/ch31/ch31_bean.fmbA Java bean bar chart whose bars query a doctor's appointments
CH31_PJCforms/ch31/ch31_pjc.fmbA phone item with a pluggable Java component
CH31_FBEANforms/ch31/ch31_fbean.fmbA Swing progress bar hosted with FBEAN

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

Three Ways to Use Java at a Glance

ApproachWhat it isUse it for
Java beanA class extending VBean, shown in a bean area item.Things Forms does not draw: a chart, a signature pad, a calendar.
Pluggable Java component (PJC)A subclass of a standard item class, named in the item's Implementation Class.Changing how a standard item behaves in the client.
FBEANA built-in package that hosts any JavaBean and calls its methods by name.JavaBeans written for any Java program, with no Forms-specific code.

Java code runs on the user's computer, like WebUtil's, and PL/SQL reaches it through properties and events. See how to use WebUtil in Oracle Forms for the other client-side toolkit. To connect a form to a web page in the user's browser instead, see how to connect a form to a web page using JavaScript.

Build a Java Bean

The sample bean form shows the bookings of the busiest doctors in November as a bar chart, and clicking a bar lists that doctor's appointments. The chart item of older releases is gone from Forms 14.1.2, as explained in how to create charts in Oracle Forms 14.1.2, but a bean area can hold any chart component written in Java.

Java bean bar chart in an Oracle Forms bean area with a doctor's appointments listed below
A Java bean chart; a click on a bar queried the doctor's appointments.

The Java Class

A bean for Forms extends oracle.forms.ui.VBean, from frmall.jar, the client's own classes.

Java class cw/BarChart.java, in carewell_beans.jar:

package cw;

import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import oracle.forms.handler.IHandler;
import oracle.forms.properties.ID;
import oracle.forms.ui.CustomEvent;
import oracle.forms.ui.VBean;

/**
 * A bar chart for a bean area of Oracle Forms.
 *   set_custom_property(item, 1, 'TITLE', 'Bookings in November');
 *   set_custom_property(item, 1, 'DATA',  'Wilson=15;Pillai=12;Menon=9');
 * A click on a bar raises the event BAR_CLICKED, with the parameter BAR_LABEL.
 */
public class BarChart extends VBean {
  private static final ID TITLE       = ID.registerProperty("TITLE");
  private static final ID DATA        = ID.registerProperty("DATA");
  private static final ID BAR_CLICKED = ID.registerProperty("BAR_CLICKED");
  private static final ID BAR_LABEL   = ID.registerProperty("BAR_LABEL");
  private static final Color BAR = new Color(0x28, 0x56, 0xb0);

  private IHandler handler;
  private String title = "";
  private String[] labels = new String[0];
  private double[] values = new double[0];
  private Rectangle[] bars = new Rectangle[0];

  public BarChart() {
    addMouseListener(new MouseAdapter() {
      public void mouseClicked(MouseEvent e) {
        for (int i = 0; i < bars.length; i++) {
          if (bars[i] != null && bars[i].contains(e.getPoint())) {
            try {
              handler.setProperty(BAR_LABEL, labels[i]);   // the event's parameter
            } catch (Exception ignored) { }
            dispatchCustomEvent(new CustomEvent(handler, BAR_CLICKED));
          }
        }
      }
    });
  }

  public void init(IHandler h) {
    super.init(h);
    handler = h;
  }

  public boolean setProperty(ID id, Object value) {
    if (id == TITLE) {
      title = String.valueOf(value);
      repaint();
      return true;
    }
    if (id == DATA) {                                      // label=value;label=value
      String[] pairs = String.valueOf(value).split(";");
      labels = new String[pairs.length];
      values = new double[pairs.length];
      for (int i = 0; i < pairs.length; i++) {
        String[] lv = pairs[i].split("=");
        labels[i] = lv[0];
        values[i] = lv.length > 1 ? Double.parseDouble(lv[1]) : 0;
      }
      repaint();
      return true;
    }
    return super.setProperty(id, value);
  }

  public void paint(Graphics g0) {
    Graphics2D g = (Graphics2D) g0;
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    int w = getWidth(), h = getHeight();
    g.setColor(Color.white);
    g.fillRect(0, 0, w, h);
    g.setColor(Color.darkGray);
    g.setFont(new Font("Dialog", Font.BOLD, 12));
    g.drawString(title, 10, 18);
    double max = 1;
    for (double v : values) max = Math.max(max, v);
    int n = values.length, left = 10, top = 30, bottom = h - 22;
    bars = new Rectangle[n];
    if (n == 0) return;
    int slot = (w - 2 * left) / n;
    g.setFont(new Font("Dialog", Font.PLAIN, 10));
    for (int i = 0; i < n; i++) {
      int bh = (int) ((bottom - top) * values[i] / max);
      bars[i] = new Rectangle(left + i * slot + 4, bottom - bh, slot - 8, bh);
      g.setColor(BAR);
      g.fill(bars[i]);
      g.setColor(Color.darkGray);
      FontMetrics fm = g.getFontMetrics();
      String v = String.valueOf((long) values[i]);
      g.drawString(v, bars[i].x + (bars[i].width - fm.stringWidth(v)) / 2, bars[i].y - 3);
      int lx = bars[i].x + (bars[i].width - fm.stringWidth(labels[i])) / 2;
      g.drawString(labels[i], lx, h - 8);
    }
  }
}

Three things connect it to Forms:

  • Properties: ID.registerProperty("DATA") declares a property PL/SQL can set by that name. Forms calls setProperty with the ID and the value; the bean handles its own properties and passes the others to VBean.
  • The handler: init receives the IHandler, the bean's link to the Forms client.
  • Events: dispatchCustomEvent sends an event to the server, where it fires the item's WHEN-CUSTOM-ITEM-EVENT trigger. A value set with handler.setProperty just before becomes a parameter of the event.

The Bean Area

A bean area is an item type of its own, with no data. Its Implementation Class property names the Java class, here cw.BarChart. When the form starts, the client creates an instance of the class in the item's rectangle, as it creates a VTextField for a text item.

Send Data to the Bean with SET_CUSTOM_PROPERTY

Syntax:

set_custom_property(item varchar2 | item_id item, row_number number, property varchar2,
                    value varchar2 | number | boolean)
get_custom_property(item varchar2 | item_id item, row_number number, property varchar2) return varchar2

The chart's data is a string of label=value pairs, built from a query when the form starts.

Example (WHEN-NEW-FORM-INSTANCE trigger, bean form):

declare
  v_data varchar2(2000);
begin
  for d in (select * from (select substr(d.first_name, 1, 1) || '. ' || d.last_name name,
                                   count(a.appt_id) n
                              from doctors d, appointments a
                             where a.doctor_id = d.doctor_id
                               and a.status = 'BOOKED'
                               and a.appt_start >= date '2026-11-01'
                               and a.appt_start < date '2026-12-01'
                             group by d.doctor_id, d.first_name, d.last_name
                             order by n desc)
             where rownum <= 8) loop                 -- no FETCH FIRST in Forms PL/SQL
    v_data := v_data || d.name || '=' || d.n || ';';           -- 'M. Wilson=15;...'
  end loop;
  set_custom_property('CTL.CHART', 1, 'TITLE', 'Bookings in November 2026');
  set_custom_property('CTL.CHART', 1, 'DATA', rtrim(v_data, ';'));
end;

row_number is the record of the bean in a multi-record block, 1 in a control block. A property name the bean does not know is passed to VBean and ignored. The labels combine the doctor's first initial and last name: grouped by last name alone, the chart first gave Wilson 19, the sum of the clinic's two Dr. Wilsons.

Receive Events with WHEN-CUSTOM-ITEM-EVENT

:SYSTEM.CUSTOM_ITEM_EVENT names the event, and :SYSTEM.CUSTOM_ITEM_EVENT_PARAMETERS names a parameter list with the values the bean set.

Example (WHEN-CUSTOM-ITEM-EVENT trigger on CTL.CHART):

declare
  v_params paramlist := get_parameter_list(:system.custom_item_event_parameters);
  v_type   number;
  v_label  varchar2(100);
begin
  if :system.custom_item_event = 'BAR_CLICKED' then
    get_parameter_attr(v_params, 'BAR_LABEL', v_type, v_label);
    :ctl.chosen := 'Dr. ' || v_label;                   -- 'M. Wilson'
    go_block('APPOINTMENTS');
    set_block_property('APPOINTMENTS', default_where,
      'doctor_id = (select doctor_id from doctors'
      || ' where substr(first_name, 1, 1) || ''. '' || last_name = '''
      || replace(v_label, '''', '''''') || ''') and status = ''BOOKED'''
      || ' and appt_start >= date ''2026-11-01'' and appt_start < date ''2026-12-01''');
    execute_query;
  end if;
end;

Clicking the bar of M. Wilson listed her booked appointments of November. Parameter lists are covered in how to pass values between forms.

Change a Standard Item with a Pluggable Java Component

A PJC is a subclass of one of the standard item classes, such as VTextField, VButton, VCheckbox, VComboBox, VRadioButton, or VTextArea, named in the item's Implementation Class. The item stays a normal item of its block, with its data and triggers; only its behavior in the client changes.

PhoneField refuses anything but digits, +, and spaces as they are typed, and colors the item until the number is complete.

Java class cw/PhoneField.java, in carewell_beans.jar:

package cw;

import java.awt.Color;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import oracle.forms.ui.VTextField;

/**
 * A pluggable Java component for phone numbers: the text item accepts only digits,
 * '+' and spaces as the user types, and turns amber until the number has ten digits.
 */
public class PhoneField extends VTextField {
  private static final Color INCOMPLETE = new Color(0xff, 0xe8, 0xb0);

  public PhoneField() {
    super();
    addKeyListener(new KeyAdapter() {
      public void keyTyped(KeyEvent e) {
        char c = e.getKeyChar();
        if (!Character.isDigit(c) && c != '+' && c != ' ' && !Character.isISOControl(c)) {
          e.consume();                                   // never reaches the server
        }
      }
      public void keyReleased(KeyEvent e) {
        String digits = getText().replaceAll("[^0-9]", "");
        if (digits.startsWith("91")) digits = digits.substring(2);
        setBackground(digits.length() == 10 ? Color.white : INCOMPLETE);
      }
    });
  }
}

In the sample PJC form, the item PHONE of a patient block has the implementation class cw.PhoneField. Typing abc in a phone number changed nothing, and the item stayed amber while digits were missing.

Oracle Forms pluggable Java component refusing letters and highlighting an incomplete phone number
A PJC: letters refused, and an incomplete number in amber.

A Convenience, Not Validation

The keystrokes the PJC consumes never reach the server: no trigger fires, and no round trip is made. That is the strength of a PJC, immediate feedback without network traffic, and its limit: it helps the user but does not validate. WHEN-VALIDATE-ITEM and the database's constraints must still check the value, as described in how to validate data in Oracle Forms.

Host Any JavaBean with FBEAN

A JavaBean written for any Java program, such as a Swing component, has no setProperty(ID, ...) for Forms to call. The built-in package FBEAN hosts it anyway: registered in a bean area with no implementation class, its public methods are called by name from PL/SQL.

Syntax:

fbean.register_bean(item, instance number, bean_class varchar2)
fbean.invoke(item, instance, method varchar2, args varchar2 | number := null)
fbean.invoke_char(item, instance, method, args) return varchar2      -- invoke_num, invoke_bool
fbean.set_property(item, instance, property varchar2, value varchar2)
fbean.get_property(item, instance, property varchar2) return varchar2
fbean.enable_event(item, instance, listener_name varchar2, subscription boolean)

The sample FBEAN form shows the progress of a long task with javax.swing.JProgressBar, from the Java runtime itself.

Example (WHEN-NEW-FORM-INSTANCE trigger, FBEAN form):

begin
  fbean.register_bean('CTL.PROGRESS', 1, 'javax.swing.JProgressBar');  -- any JavaBean
  fbean.invoke('CTL.PROGRESS', 1, 'setMaximum', 100);
  fbean.set_property('CTL.PROGRESS', 1, 'StringPainted', 'true');     -- setStringPainted
end;

Example (WHEN-BUTTON-PRESSED trigger on CTL.STEP):

:ctl.done := least(nvl(:ctl.done, 0) + 25, 100);
fbean.invoke('CTL.PROGRESS', 1, 'setValue', :ctl.done);
:ctl.info := 'getValue returned ' || fbean.invoke_num('CTL.PROGRESS', 1, 'getValue');
Swing JProgressBar hosted in Oracle Forms with the FBEAN package
A Swing progress bar hosted by FBEAN.

INVOKE passes the value to setValue, and INVOKE_NUM returns what getValue returns. Arguments are text or numbers: a BOOLEAN argument did not compile (PLS-00306), and SET_PROPERTY with 'true' called setStringPainted(true). ENABLE_EVENT subscribes to the bean's events, by the listener interface's name, such as ActionListener, and they then fire WHEN-CUSTOM-ITEM-EVENT.

Build and Deploy the JAR

Compile the Java classes against frmall.jar, in the forms/java directory of the Forms installation, and package them in a JAR.

Compile and package:

javac --release 11 -cp $ORACLE_HOME/forms/java/frmall.jar -d classes cw/*.java
jar cf carewell_beans.jar -C classes cw

--release sets the oldest Java version that can load the classes, so choose that of the oldest Java runtime your users' clients run; 11 was used here, with a client running Java 21. Copy the JAR to forms/java and add it to the archive parameter of the configuration section, as with button icons.

The formsweb.cfg section:

[book]
archive=frmall.jar,carewell_icons.jar,carewell_beans.jar

The Forms Standalone Launcher downloads the JAR into its cache on the first run, and again whenever it changes; its log showed Downloading archive file carewell_beans.jar after the PJC was added. The test JAR was not signed, and the launcher loaded it. Oracle's JARs are signed, and for the browser and Java Web Start, Oracle's documentation requires your JARs to be signed with a code-signing certificate too, so sign them for every client and users never see a security question. Icon JARs are described in how to create push buttons in Oracle Forms.

Conclusion

The Oracle Forms client is Java, so bean areas can host your own components and PJCs can replace the classes of standard items. A bean extends VBean, registers the properties PL/SQL sets with SET_CUSTOM_PROPERTY, and raises events that fire WHEN-CUSTOM-ITEM-EVENT, with parameters in :SYSTEM.CUSTOM_ITEM_EVENT_PARAMETERS. A PJC gives immediate feedback without round trips but does not replace validation, FBEAN hosts any JavaBean and calls its methods by name, and every class ships in a JAR compiled against frmall.jar, listed in the archive, and signed.

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
11