You are on page 1of 11

Developer: Java

DOWNLOAD
Integrating Oracle Forms into Oracle JDeveloper &
ADF
Oracle ADF Faces
TAGS

by Wilfred van der Deijl java, forms, All

Learn a technique for extending existing Forms applications using Oracle ADF components.

Published June 2007

Oracle Forms has been Web-enabled for years. It is perfectly possible to run Oracle Forms in a Web
browser, and in its 2005 Statement of Direction, Oracle indicated that it remains committed to the
development and support of Oracle Forms.

On the other hand, there is rapid growth in and adoption of Java/JEE technologies and a general shift toward
service-oriented architecture (SOA). With Oracle JDeveloper and Oracle Application Development
Framework (ADF), it is possible to build feature-rich JavaServer Faces (JSF) Web applications. This article
describes a combination of techniques that enables you to integrate these two worlds.

Oracle ADF is an innovative J2EE development framework available in Oracle JDeveloper. It simplifies
Java development by minimizing the need to write code that implements well known design patterns and the
application's infrastructure. Oracle ADF provides these implementations as part of the framework.

This article focuses on ADF Faces, one of the components of the ADF Framework. ADF Faces contains a
large set of UI components built on top of the standard JSF APIs that leverage the latest technologies—
including partial page rendering and Ajax—to provide a rich, interactive user interface. Although this article
assumes the use of ADF Faces, the same techniques can also be used to integrate Oracle Forms with other
Web technologies, such as PHP, Oracle Application Express, or .Net ASP.

In this article I'll present an extreme example of integrating Oracle Forms and Oracle ADF Faces in such a
way that end users won't notice the difference between the Oracle ADF Faces user interface and the pages
that incorporate an existing Oracle Forms form. See Figure 1 for a simple example of such integration. Only
the multirecord block showing the orders is an existing Oracle Forms form. The rest of the page comprises
Oracle ADF Faces components.
Figure 1 Oracle Forms form integrated into Oracle ADF Faces

This example demonstrates several things:

 Visually, the form is tightly integrated with the Oracle ADF Faces page. The menu, toolbar, and status
bar are not shown. This integration changes the user experience from what is typical in Oracle Forms
to a more classic Web experience. It enables you to gradually adopt Oracle ADF Faces while still
leveraging features of your Oracle Forms applications. In this example, it is easy to replace the Oracle
Forms form with an Oracle ADF Faces table without users noticing. For some situations, this extreme
integration might be too much and you could leave users with a menu, toolbar, and/or status bar if you
like. You could just as well go with a solution in which users stay with the well-known multidocument
interface of Oracle Forms, with callouts to Oracle ADF Faces pages for specific tasks.

 What you cannot clearly see in the screen shot is that only orders for a particular customer are shown.
The currently selected customer is on the (pure Oracle ADF Faces) Customers tab, which means that
the ID of the selected customer is passed to Oracle Forms to query the appropriate records.

 Oracle Forms also passes its context back to Oracle ADF Faces. When you scroll through the
multirecord Orders block, the detail Oracle ADF Faces table showing the order lines requeries
automatically. This proves that the order ID is passed back to Oracle ADF Faces and that Oracle
Forms can trigger events (such as the refresh of the detail table) in Oracle ADF Faces. In this example,
the Oracle ADF Faces table is on the same page, but it could just as easily have been on a different
Web page. The key concept is that Oracle Forms passes context to Oracle ADF Faces.
 The Oracle ADF Faces page also has a Save Form submit button. That Oracle ADF Faces button
sends an event to Oracle Forms to instruct it to perform a commit, which shows that it is possible to
raise Oracle Forms events from within Oracle ADF Faces.

 Another thing this screen shot does not show is the elimination of the Oracle Forms applet startup
times. Oracle Forms uses a Java applet in the client browser. Starting this applet takes a couple of
seconds. When a user navigates between Oracle ADF Faces pages and some of them include an
Oracle Forms form, you don't want the user to experience this startup time for each page that includes
such a form.

Including the Applet


The first hurdle is to include the actual Oracle Forms form on an Oracle ADF Faces Web page. See Figure 2
for an overview of how an Oracle Forms form runs in the client browser. The figure shows how,
conceptually, Oracle Forms (green block) runs as a Java applet (blue block) on a Web page (yellow block) in
the client's browser.

Figure 2 Oracle Forms applet on a Web page

Typically this entire Web page is served by the Oracle Forms servlet. In this concept, you no longer rely on
this servlet to supply the necessary HTML. The HTML required to start the applet needs to be incorporated
into true Oracle ADF Faces Web pages.

You could create a JSF component to render this HTML and include the component on your Oracle ADF
Faces pages. For now, use the easier route of defining a region and reusing it on all of your Oracle ADF
Faces pages. First you create a JSPX file that defines the Oracle ADF Faces tags to output the Oracle Forms
HTML to the client:

<?xml version='1.0' encoding='windows-1252'?>


<af:regionDef var="regionParams"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:af="http://xmlns.oracle.com/adf/faces">
<f:verbatim>
. . . { actual HTML and JavaScript goes here } . . .
</f:verbatim>
</af:regionDef>

Next, register this region in the META-INF/region-metadata.xml file:

<component>
<component-type>com.example.oracleFormRegion</component-type>
<component-class>oracle.adf.view.faces.component.UIXRegion</component-class>
<component-extension>
<region-jsp-ui-def>/regions/oraFormsRegion.jspx</region-jsp-ui-def>
</component-extension>
<attribute>
<attribute-name>formModuleName</attribute-name>
<attribute-class>java.lang.String</attribute-class>
</attribute>
</component>

Finally, you can reuse this definition with <af:region> on every Oracle ADF Faces page that needs to embed
an Oracle Forms form:

<af:region id="oraFormRegion" regionType="com.example.oracleFormRegion">


<f:attribute name="formModuleName" value="orders.fmx" />
</af:region>

The use of <af:regionDef> and <af:region> enables you to define the HTML and JavaScript required to start
Oracle Forms only once and reuse it on every Oracle ADF Faces page in which you want to embed an
Oracle Forms form.

Inbound JavaScript API


The key requirement is a technique to link Oracle Forms and Oracle ADF Faces. It should be possible for
both environments to communicate with each other. This ability is necessary for passing context (such as the
ID of the currently selected record) to the other environment and even to raise events in the other
technology. You can achieve this with a JavaScript-based API. Oracle ADF Faces can include JavaScript to
call into Oracle Forms, and Oracle Forms should be able to execute JavaScript to communicate with Oracle
ADF Faces.

The first technique I describe here is use of an inbound JavaScript API that enables Oracle ADF Faces to
raise an event in Oracle Forms and thereby execute PL/SQL code in Oracle Forms. A simple example is an
Oracle ADF Faces Save button that both commits the Oracle Forms form and submits and saves the Oracle
ADF Faces input items on the same page.

Oracle Forms 10g Release 2 and previous versions do not have a native JavaScript API. This is a feature
whose introduction is planned with the release of Oracle Forms 11g, but you can implement this feature
yourself in Oracle Forms 10g Release 2 (or previous versions) by using the industry-standard LiveConnect
API, which is implemented by all major browsers. This API gives JavaScript the ability to call public
methods of Java classes. The Oracle Forms applet is a Java class, and if this class exposes a public method,
it can be called from JavaScript.

The Oracle Forms applet as shipped by Oracle does not offer a public method, but you can add one yourself.
Sub-class the Oracle Forms applet class oracle.forms.engine.Main, and add a public raiseEvent() method.
This new public method can be called from the client-side JavaScript and receive a payload (message) to
pass on to Oracle Forms (see step 1 in Figure 3):
document.formsapplet.raiseEvent('do_key', 'commit_form');

Figure 3 Inbound JavaScript API

This public raiseEvent() method still needs to trigger code in the active Oracle Forms form. This is not
something you can do directly. Fortunately Oracle Forms offers a framework called Pluggable Java
Components (PJCs) for doing something like this. A PJC is a JavaBean that sub-classes
oracle.forms.ui.VBean, which can be included in an Oracle Forms form. We can create a generic PJC to
handle this inbound JavaScript communication. Make sure to embed this PJC in each form. Doing so can be
a simple automated task with the Java Development API (JDAPI). The public method from the extended
Oracle Forms applet can get a handle on the PJC in the active form and pass the payload on to that PJC (see
step 2 in Figure 3):

public void raiseEvent(String event, String payload) {


CommunicatorBean communicator = findFirstCommunicator();
if (null != communicator) {
communicator.sendMessageToForms(event, payload);
}
}

A PJC can trigger an Oracle Forms trigger called WHEN-CUSTOM-ITEM-EVENT (see step 3 in Figure 3):

public void sendMessageToForms(String event, String payload) {


try {
// set properties to pass to Oracle Forms
mHandler.setProperty(PROP_EVENT, event);
mHandler.setProperty(PROP_PAYLOAD, payload);
// trigger WHEN-CUSTOM-ITEM-EVENT trigger in Forms
CustomEvent ce = new CustomEvent(mHandler, EVENT_MSG_TO_FORMS);
dispatchCustomEvent(ce);
} catch (FException e) {
e.printStackTrace();
}
}

Finally, you can use PL/SQL code to inspect the payload of the event and act accordingly:

declare
BeanEventDetails ParamList;
ParamType number := text_parameter;
Event varchar2(1000);
Payload varchar2(1000);
begin
BeanEventDetails := get_parameter_list(:system.custom_item_event_parameters);
get_parameter_attr(BeanEventDetails, 'Event', ParamType, Event);
get_parameter_attr(BeanEventDetails, 'Payload', ParamType, Payload);
if event='do_key' then
do_key(payload);
end if;
end;

This completes the path from the client-side JavaScript to the Oracle Forms-side PL/SQL code. It starts with
the client-side JavaScript's calling a public method of the extended Oracle Forms applet and passing a
payload (step 1). The public method then gets a handle on the PJC of the active Oracle Forms form and calls
a public method of this PJC to pass the payload (step 2). The PJC subsequently triggers the WHEN-
CUSTOM-ITEM-EVENT trigger, again passing the payload (Step 3). Finally, the PL/SQL code handling
the trigger can inspect the payload and act accordingly.

Outbound JavaScript API


Next you need Oracle Forms to be able to trigger events on the Web page on which it is hosted. This can be
used to pass context and/or events back from Oracle Forms to the Web application. A typical example might
be scrolling through a multirecord block in Oracle Forms while passing the ID of the selected record back to
the Web application. This can be used to show details of the selected object in the Web application.

This communication can be accomplished with what we call an outbound JavaScript API, an API that
enables Oracle Forms to execute and evaluate JavaScript from PL/SQL.

This API works with the same PJC we already used for the inbound JavaScript API. Using the
SET_CUSTOM_PROPERTY built-in from PL/SQL, you can pass parameters to the PJC (see step 1 in
Figure 4):

begin
-- set the Order ID
set_custom_property('BLK_PJC.PJC', 1, 'EvalExpression',
'document.getElementById(''frm:ordid'').value=' || :ord.ordid);
end;
Figure 4 Outbound JavaScript API

In this case, the property of the PJC would be the JavaScript to be evaluated or executed. The PJC can then
get a handle on its containing Java applet (see step 2 in Figure 4). This applet offers a method for evaluating
the JavaScript as part of the LiveConnect API (see step 3 in Figure 4):

public boolean setProperty(ID property, Object value) {


if (PROP_EVAL_EXPR == property) {
JSObject appletWindow = JSObject.getWindow(mHandler.getApplet());
Object evalResult = appletWindow.eval(value.toString());
// make result available as PJC property
setProperty(PROP_EVAL_RESULT, evalResult);
return true;
}
}

This example just sets the value of a (probably hidden) input element on the Web page. You can use the
same outbound JavaScript API to submit the Web page back to the Web server. This is effectively the same
as a user's entering the Order ID in a search form and clicking the Submit button.

Suspend and Reuse the Applet


Oracle Forms uses a Java applet that runs in the Web browser to display the form and handle user
interaction. Starting a large Java applet such as an Oracle Forms form can be resource-intensive and can take
a couple of seconds. In a typical Oracle Forms installation, this does not matter, because you start the Oracle
Forms applet at the beginning of your session and continue working with it for a period of time without
navigating to another Web page. However, in the architecture this article introduces, a user can navigate
between Web pages. Some of these might include an Oracle Forms form, and others might not. The default
behavior of your Web browser and Sun Microsystems' Java Virtual Machine is to destroy the applet when
you navigate away from the page.

When using Java Virtual Machine version 1.4.2 or higher on a client, you can specify an additional
parameter called legacy_lifecycle in the applet's HTML. This parameter instructs the virtual machine not to
destroy the applet when you navigate away from the page. Instead, the applet is kept running in the
background and is put into something called the legacy lifecycle cache. The applet is reactivated when the
user returns to a Web page that includes exactly the same applet declaration. This means that the applet's
HTML has to be 100 percent identical for the lifecycle cache to resume an old applet. This is another reason
you want to declare the HTML only once in a JSF region or JSF custom component.

Using this feature means that the applet is started only once for the entire browser session. The startup time
of the applet is completely eliminated when the user returns to a page that includes an Oracle Forms form.

Act on Applet Reactivation


An applet resumes from this cache in exactly the same state it was in when it was suspended. This means
that the Oracle Forms form from the previous Web page is still running. You probably want to add actions to
the reactivation of the applet. Unfortunately, no Oracle Forms trigger fires during applet reactivation. The
deprecated show() method of the Oracle Forms applet is called during applet reactivation. You can override
this method as follows:

public void show() {


super.show();
raiseEvent("WHEN-APPLET-ACTIVATED", "");
}

This uses the inbound JavaScript API to send an event to Oracle Forms and can be handled in the WHEN-
CUSTOM-ITEM-EVENT trigger of the PJC:

declare
BeanEventDetails ParamList;
ParamType number := text_parameter;
Event varchar2(1000);
Payload varchar2(1000);
begin
BeanEventDetails := get_parameter_list(:system.custom_item_event_parameters);
get_parameter_attr(BeanEventDetails, 'Event', ParamType, Event);
get_parameter_attr(BeanEventDetails, 'Payload', ParamType, Payload);
if event='WHEN-APPLET-ACTIVATED' then
-- add handling here
end if;
end;

Need for a Landing Form


You can have different Oracle ADF Faces pages in your application that all include an Oracle Forms form.
These different pages probably want to call different forms. You cannot pass the names of these forms as
normal parameters in the applet's HTML. Doing so would require different HTML on each Web page, which
would defeat the purpose of the legacy lifecycle feature. That feature requires 100 percent identical HTML
to enable reuse of the suspended applet.

You can circumvent this requirement by always specifying the same "landing form" as the name of the
Oracle Forms form. The name of the actual form to be started can be included in a hidden element on the
Web page:
<input type="hidden" id="formname" value="orders.fmx"/>

The WHEN-NEW-FORM-INSTANCE trigger of the landing form can ascertain which form is actually
needed, by evaluating document.getElementById('formname').value, using the outbound JavaScript API.
The landing form can then start the requested form:

declare
formName varchar2(1000);
begin
while true loop
-- get the form name
set_custom_property('BLK_PJC.PJC', 1, 'EvalExpression',
'document.getElementById(''frm:oraFormRegion:formname'').value');
formName := get_custom_property('BLK_PJC.PJC', 1, 'EvalResult');
-- start the form
call_form(formName);
if (get_custom_property('BLK_PJC.PJC', 1, 'AppletActive')='FALSE' then
-- exit the loop if the user is closing the browser
exit;
end if;
end loop;
end;

When the applet is reactivated from the lifecycle cache, the "real" form is running. That form should handle
the applet reactivation by exiting itself. This returns control to the endless loop in the landing form, which
then inspects the hosting Web page again to ascertain which form should be started. The only way this loop
ends is when the applet is no longer active because the user is closing the browser.

Getting Context into Oracle Forms


The legacy lifecycle feature introduces a problem when passing context from the Web application to Oracle
Forms. What if you want to incorporate an Oracle Forms form to edit an order in an Oracle ADF Faces
page? You probably want to pass the selected order ID from the Oracle ADF Faces application to Oracle
Forms. Again, you cannot do this the typical way by adding the parameter in the HTML to include the
Oracle Forms applet. This would mean different HTML for each order and would defeat the reuse of the
suspended applet.

Once more , you can work around this issue with the outbound JavaScript API. You can incorporate the
order ID (or whatever context you want to pass) as an invisible item on the Oracle ADF Faces pages. The
Oracle Forms form can inspect this value during the WHEN-NEW-FORM-INSTANCE trigger and act
accordingly:

declare
orderID number;
begin
-- get the order ID
set_custom_property('BLK_PJC.PJC', 1, 'EvalExpression',
'document.getElementById(''frm:orderID'').value');
customerID := get_custom_property('BLK_PJC.PJC', 1, 'EvalResult');
:parameter.ordid:=orderID;
-- execute query (where clause refers :parameter.ordid)
do_key('execute_query');
end;

Visual Integration
You can opt to include a single form on an Oracle ADF Faces page and not allow users to navigate to other
Oracle Forms forms. You might even go as far as making the users believe it's not even Oracle Forms they
are using. For this, you might want to remove the menu, toolbar, status bar, and Oracle Forms edges.

The easiest way to achieve all this is by clipping the Oracle Forms applet. Surround the applet HTML with a
single <div> element, which represents a rectangular area. The height and width of this <div> can be set
smaller than the size of the Oracle Forms applet it contains. Also, the x and y position of the Oracle Forms
applet can be set to negative values relative to the surrounding <div> element, which results in a small
viewing window that shows only the relevant portion of the Oracle Forms form. This is demonstrated by the
area boxed in red in Figure 5:

Figure 5 Clipped Oracle Forms form with a limited view port

Conclusion
As you can see, it is possible to extend an existing Oracle Forms application using ADF Faces. You can use
ADF Faces today while protecting your Forms investment of yesterday.

You can also use the technique to switch from an Oracle Forms user experience to a more classic Web user
experience. In that scenario, you would visually remove the menu, tool bar, and status bar from all Oracle
Forms forms. Initially, you reuse existing Oracle Forms forms as single objects on Oracle ADF Faces Web
pages. All application flow control is handled by Oracle ADF Faces, instantly giving users an intuitive Web
experience while enabling you to reuse existing forms. This approach enables you to adapt an Oracle ADF
Faces front end to your applications one form at a time.

The integration also enables the building of portlets that embed Oracle Forms forms. These portlets could be
used in an Oracle Portal environment or with the just released Oracle WebCenter technology.

Another use of this technology is to embed individual Oracle Forms forms in Web pages to complete a
BPEL human task. Oracle BPEL can monitor and control your business process. A business process might
have a human task, such as a manager approving or declining a purchase order, at a certain stage. With the
technology from this article, you can reuse the existing Oracle Forms form to edit a purchase order in the
BPEL human task.

As you can see, the technology described here is potentially the basis for a whole array of new possibilities.
If you just let the idea sink in and give it some thought, you probably can come up with other uses within
your own organization. Go to www.oratransplant.nl/oracle-forms-as-web-component to get more details on
this technique; you can get a step-by-step guide to building your own sample integration, read detailed
papers, and get sample applications.

Wilfred van der Deijl is the senior system architect for Eurotransplant. He has more than 12 years of
experience with Oracle tools. He runs an Oracle-related Weblog, OraTransplant, and is a member of Oracle's
Customer Advisory Board for Development Tools.

You might also like