26. Mai 2008

Open-Form benötigt eine Exit-Form-Strategie

Dieser Artikel soll all jenen weiterhelfen, die mit OPEN_FORM statt CALL_FORM arbeiten.

OPEN_FORM hat Vorteile aber auch ein paar Probleme. Der Anwender kann zwischen allen offenen Masken hin und herspringen, beendet er jedoch eine Maske, wird immer nur die aktuelle Maske geschlossen.

Lösung: Gehen wir einmal von einer normalen Applikation aus, in der es eine Startmaske gibt, von der aus alle anderen Masken per OPEN_FORM geöffnet werden. Wenn die Startmaske den Fokus hat und man klickt auf Beenden, dann möchte man die gesamte Applikation beenden und nicht nur die Maske in der man sich befindet. Wir brauchen an der Stelle eine Funktionalität, die zuerst einmal in einer Schleife durch alle geöffneten Masken läuft, die schliesst und am Ende die Startmaske beendet.

Alle geöffneten Masken zu finden ist nicht einfach. Am besten speichert man diese Daten in einer globalen Liste namens GLOBAL.OPEN_FORMS. Die Werte werden durch Semikolon getrennt gespeichert, z.B. ;STARTFORM;EMP;DEPT;

Wir brauchen nun einen PRE-FORM und einen POST-FORM-Trigger für alle Masken, außer der Startmaske. Der Pre-Form hängt einen neuen Eintrag ans Ende der Liste und der Post-Form löscht entsprechende Einträge wieder raus.

PRE-FORM :

DEFAULT_VALUE (';', 'GLOBAL.OPEN_FORMS');
:GLOBAL.OPEN_FORMS := :GLOBAL.OPEN_FORMS ||
:SYSTEM.CURRENT_FORM || ';';

POST-FORM :

:GLOBAL.OPEN_FORMS := REPLACE (:GLOBAL.OPEN_FORMS,
';' || :SYSTEM.CURRENT_FORM || ';',
';');

Nun haben wir die Namen aller geöffneten Masken in der globalen Liste gespeichert.

Der KEY-EXIT Trigger in der Startmaske arbeitet in einer Schleife nun alle Listeneinträge ab und schliesst diese Masken.


KEY-EXIT
der Startmaske :

DECLARE
V_Form VARCHAR2 (30);
BEGIN
One_Time_Timer.Initialize ('EXIT_STARTFORM');
DEFAULT_VALUE (';', 'GLOBAL.OPEN_FORMS');
WHILE :GLOBAL.OPEN_FORMS != ';'
LOOP
V_Form := Substr (:GLOBAL.OPEN_FORMS,
2,
InStr (:GLOBAL.OPEN_FORMS, ';', 1, 2) - 2);
COPY ('J', 'GLOBAL.EXIT_IMMEDIATE');
GO_FORM (V_Form);
END LOOP;
END;

Die Startmaske wird durch einen WHEN-TIMER-EXPIRED beendet:

IF One_Time_Timer.Get_Value = 'EXIT_STARTFORM' THEN
EXIT_FORM (no_validate);
END IF;

Alle anderen Masken benötigen noch einen WHEN-FORM-NAVIGATE Trigger:

DEFAULT_VALUE ('N', 'GLOBAL.EXIT_IMMEDIATE');
IF :GLOBAL.EXIT_IMMEDIATE = 'J' THEN
EXIT_FORM (no_validate);
END IF;


Nun haben wir eine gut funktionierende AutoClose-Methode für alle unsere Applikationen.

Viel Spass damit
Gerd


Hier ist der One-Time-Timer Artikel. Der Einfachheit halber hier direkt der Code für die Startmaske:

PACKAGE Const IS
gbl_One_Time_Timer CONSTANT VARCHAR2 (61) :=
upper ('global.One_Time_Timer');
END;

PACKAGE One_Time_Timer IS
FUNCTION Get_Value RETURN VARCHAR2;
PROCEDURE Initialize (P_Event IN VARCHAR2);
END;

PACKAGE BODY One_Time_Timer IS
FUNCTION Get_Value RETURN VARCHAR2 IS
BEGIN
Default_Value (NULL, Const.gbl_One_Time_Timer);
RETURN (NAME_IN (Const.gbl_One_Time_Timer));
END;

PROCEDURE Initialize (P_Event IN VARCHAR2) IS
tm_id timer;
tm_name VARCHAR2 (30) := 'ONE_TIME_TIMER';
BEGIN
tm_id := Find_Timer (tm_name);
IF ID_Null (tm_id) THEN
tm_id := Create_Timer (tm_name, 10, NO_REPEAT);
COPY (p_Event, Const.gbl_One_Time_Timer);
END IF;
END;
END One_Time_Timer;

14. April 2008

Forms 11g - Umfrage

Nachdem ich nun die letzten Artikel über Forms 11g und die neuen Features geschrieben habe, interessiert mich nun, welches Features davon in der täglichen Arbeit direkt eingesetzt werden wird.

Desweiteren würde mich interessieren, welche anderen Features in den nächsten Patches und Releases integriert werden.

Antworten bitte direkt als Kommentare zu diesem Post.

Danke
Gerd

26. März 2008

Forms 11g New Features: Javascript-API

Forms 11g erlaubt erstmals eine direkte Kommunikation zwischen dem generischen Java-Applet des Browsers und der umgebenden Welt. Das neue JavaScript-API ermöglicht dies.

In der neuen Version gibt es zur Kommunikation mit dem JavaScript-API einen neuen Forms-Trigger, neue Systemvariablen und neue Built-Ins.

Der Trigger WHEN-CUSTOM-JAVASCRIPT-EVENT reagiert auf alle Events, die per JavaScript von außen an Oracle Forms herangetragen werden. Innerhalb dieses Triggers kann man die Payload aus zwei neuen Systemvariablen auslesen. system.javascript_event_name und :system.javascript_event_value.


Informationen, die von einer HTML-Seite an Forms weitergereicht werden, kann man nun ganz einfach auslesen:


In diesem kleinen Beispiel wird in der Payload als Event-Name der Befehl „NewForm“ übergeben und im Event-Value der Name der Maske. Die Daten werden zum Beispiel wie folgt aus der Internetseite übergeben:

< INPUT id="outside_field_id">
< SCRIPT>
function set_field (field_id, myValue) {
document.getElementById(field_id).value=myValue;
};
function clickEvent1()
{
document.forms_applet.raiseEvent("NewForm", "payload");
}
< /SCRIPT>
< INPUT id="button1" type="button" onClick="void clickEvent1();" value="NewForm">

Benutzt wird hier die Methode raiseEvent der Klasse forms_applet. Dieses Applet muss zuvor im OAS in der Konfigurationsdatei formsweb.cfg dem Systemparameter applet_name zugewiesen worden sein.

applet_name=forms_applet

Forms ist in der Lage bidirektional mit dieser Internetseite zu kommunizieren. Dazu nutzt man die neuen Built-Ins web.javascript_eval_expr und web.javascript_eval_function.

web.javascript_eval_expr
('document.getElementById("outside_field_id").value="' ||
:control.ti_inside || '";');
web.javascript_eval_expr
('set_field("outside_field_id", "' || :control.ti_inside
|| '")');
:control.ti_get_value := web.javascript_eval_function
('document.etElementById("outside_field_id").value');

Dieser Sourcecode befüllt in der Internetseite ein Feld namens „outside_field_id“ mithilfe der Built-In web.javascript_eval_expr. Zwei Techniken können hierbei genutzt werden. Die direkte Zuweisung oder der Aufruf einer JavaScript-Funktion, z.B. „set_field“.
Felder können ausgelesen werden durch die Nutzung der Built-In web.javascript_eval_function. Der Returnwert ist der Wert des entsprechenden Feldes in der HTML-Seite, hier wieder exemplarisch „outside_field_id“.

Dies ist ein weiteres Beispiel dafür, wie wichtig die Neuerungen in Forms 11g sind. Endlich kann Forms –aus dem Korsett des generischen Java-Applets heraus – mit der umgebenden Welt kommunizieren!

8. Februar 2008

LOV mit gesplitteten Daten

Eine interessante Art und Weise um LOV-Daten zu visualisieren ist diese hier:



Benutze so viele UNION ALL's wie nötig um die Daten zu konkatinieren:

select '---new colleagues---' ename, NULL job, NULL hiredate
from dual
UNION ALL
select ename, job, to_char (hiredate, 'DD.MM.YYYY')
from emp where hiredate >= to_date ('01.07.1981', 'DD.MM.YYYY')
UNION ALL
select '---before 07/81---' ename, NULL job, NULL hiredate
from dual
UNION ALL
select ename, job, to_char (hiredate, 'DD.MM.YYYY')
from emp where hiredate < to_date ('01.07.1981', 'DD.MM.YYYY')

Viel Spass damit
Gerd

11. Januar 2008

Hotkey F1 in Forms 10g

Seit der Forms-Version 10g ist der Hotkey F1 ein wenig problematisch. Er funktioniert so ohne weiteres nicht mehr.

Im Hotkey-Mapping für KEY-HELP zum Beispiel wurde nun Ctrl+H genommen statt dem guten alten F1.

Wenn man dies wieder auf den gleichen Stand wie unter Forms 6i bringen möchte, kann man die fmrweb.res und fmrwebd.res anpassen :

Die interne ID des Hotekeys F1 ist 112, die interne ID des KEY-HELP-Trigger ist 30:

...
113 : 0 : "F2" : 95 : "List Tab Pages"
112 : 0 : "F1" : 30 : "Help"
72 : 2 : "Ctrl+H" : 30 : "Help"

viel Spass damit
Gerd

13. Dezember 2007

Talk2Gerd in Deutsch

Heute lernte ich von Patrick Wolf, wie einfach es sein kann, einen Blog in mehreren Sprachen zu schreiben. Dies hier ist nun mein guter alter Talk2Gerd auf Deutsch. Die englische Version findet man hier.

Abschliessend noch ein Foto von meiner Firma im tiefen Schnee... und wir haben hier ne Menge Schnee in den letzten Wintern gehabt:

12. Dezember 2007

Libraries: PLL vs. PLX

Ein grosses Problem beim Erstellen von Forms-Anwendungen ist: Soll ich Libraries auf Basis von *.pll oder *.plx attachen?

Schauen wir uns einmal ein paar typische Fälle an:

A) Für die Forms-Entwicklung werden PLL und PLX genutzt. Der Forms Builder benutzt das Arbeitsverzeichnis C:\Forms und der FORMS_PATH hat einen Pfad auf C:\Forms\Lib. Die Sourcecodes heissen Lib.pll, Menu.mmb und EMP.fmb.

Wenn es eine generierte Lib.plx in c:\forms oder c:\forms\lib gibt, dann ist dies die Version, die zur Runtime benutzt wird.

Die Reihenfolge, in der Forms Libraries nutzt ist folgende:
1) Wenn Lib.plx im Arbeitsverzeichnis liegt wird es genutzt
2) Wenn es nicht gefunden wird, suche im FORMS_PATH nach Lib.plx
3) Wenn es nicht gefunden wird, suche im Arbeitsverzeichnis nach Lib.pll
4) Wenn es nicht gefunden wird, suche im FORMS_PATH nach Lib.pll
5) Wenn die Library nicht gefunden wird, erzeugt Forms eine Fehlermeldung

Dies gilt auch für Forms und Menüs



Wenn das PLX im lokalen Verzeichnis eine ältere Version ist als die entsprechende PLL, dann führt dies zu grossen Problemen während der Laufzeit. Fehler zu finden ist dann nahezu unmöglich, solange der Entwickler die Timestamps der Libraries nicht überprüft.

B) Wenn in der Anwendung nur PLL's benutzt werden:

Während der Entwicklung und zur Laufzeit hat man keine Probleme. Wenn eine Library nicht im Arbeitsverzeichnis liegt, wird sie im FORMS_PATH gefunden.

Zusammenfassung:

Ich selber nutze keine plx-Libraries mehr, da ich in viel zu vielen Projekten mit den Problemen konfrontiert war. Fehlersuche wird immens aufwändig, wenn man grössere Forms-Pfade hat und irgendwo eine plx-Library existiert, die nicht die richtige Version besitzt.

Viele Entwickler sagen: plx ist schneller. Dies konnte ich bisher nicht nachvollziehen und ich hatte auch noch nie Performance-Probleme damit.

Andere Entwickler sagen: plx ist der kompilierte Code, somit kann niemand den Sourcecode stehlen - in diesem Fall sollte man jedoch einmal folgendes ausprobieren:

Erzeuge eine Library mit einem Package und einer Konstanten:
PACKAGE Const IS
HiddenPW                     CONSTANT Varchar2 (100) := 'HiddenPW';
END Const;

Erzeuge die plx und öffne sie in einem Editor. Suche nach "HiddenPW". Der Name der Konstanten und ihr Wert sind auf diese Weise direkt zu finden. Das ist alles andere als sicher. Nur der Sourcecode ist unleserlich. Meines Erachtens ist eine Forms-Anwendung auf einem Oracle Application Server sicher. Somit braucht man den lesbaren Code der pll nicht in eine plx umzuwandeln

Dies gilt natürlich ausdrücklich nicht für Standard-Software, die man verkauft und deren Sourcecode man nicht ausliefern möchte. Hier ist die plx die einzige Alternative und auf der Datenbank sollte man über ge-wrappte Packages nachdenken.

11. Dezember 2007

DOAG Top News: Forms 11g

Die DOAG hat als neue Top News einen meiner Forms 11g Abstracts abgedruckt.

Dort schrieb ich über die neuen Features von Forms 11g und den vielfältigen Möglichkeiten, die uns in der künftigen SOA Welt zur Verfügung stehen.

Forms 11g unterwegs in Richtung SOA :

Donnerstag, 6. Dezember 2007 (wta) – Nach über sechs Jahren bringt die neue Forms-Version 11g erstmals wieder Neuerungen im Forms Builder. Die DOAG hat vorab einen Blick darauf geworfen und zwei extrem wichtige Funktionalitäten entdeckt.

Die erste betrifft die Kommunikation mit Advanced Qeueing aus der Datenbank. Es gibt einen neuen Objekttyp "Event", der mit einer Queue zusammenarbeitet. Werden Daten in eine Datenbank-Queue gestellt, startet in der Formsmaske sofort ein Event, der abgearbeitet wird.

Das zweite Highlight bezieht sich auf die Javascript-Integration. HTML-Seiten können nun direkt per Javascript mit Oracle Forms kommunizieren. Früher war dies nur über Umwege oder Java-Applets möglich. Mit der neuen Technik werden in Forms "WHEN CUSTOM JAVASCRIPT EVENT"-Trigger gestartet, die vom Entwickler benutzt werden können. Die Kommunikation verläuft bidirektional. Somit können auch Änderungen in der HTML-Seite direkt von Forms via Javascript vorgenommen werden.

Diese beiden neuen Techniken sind deutliche Anzeichen dafür, dass Oracle Forms mit neuem Schwung in die Zukunft durchstartet. In der neuen Oracle Fusion Middleware 11g ist Forms einer der zentralen SOA-Bestandteile.

Weitere Informationen zu Forms 11g finden Sie bei der Deutschen Forms Community sowie im Blog des Betatesters für die DOAG.


Vielen Dank an die DOAG !

1. Dezember 2007

Neues Statement of Direction, Nov. 2007

Im aktuellsten SoD wird Oracle Forms bis mindestens 2013 supported:

26. November 2007

Forms 11g New Features: Events

Mein Vortrag auf der DOAG Konferenz 2007 war "Forms 11g: Ein Blick hinter die Kulissen"

Die zwei neuen Features sind: Advanced Queuing und Javascript-API. Hier beschreibe ich nun die AQ-Technik mit den neuen Forms-Events.

Events - Interaktion mit Advanced Queuing

Dies ist die erste grosse Änderung im Forms Builder seit 6 Jahren. Wir können Events erstellen und Queues zuweisen. Sobald neue Daten in einer AQ ankommen, wird der neue Trigger WHEN-EVENT-RAISED gestartet.


Erlaubte Properties für Events

Event Type : Data Base, User Defined
Subscription Name : AQ-Name (LOV)
Scope : Application, Form
Auto Subscribe : Yes, No
Correlation ID :
View Mode : Browse, Locked, Removed

formsweb.cfg

maxEventWait=1000

Der neue Parameter maxEventWait ist sehr wichtig. Der Wert gibt in Millisekunden das Zeitintervall an, in dem die AQ's überprüft werden. Ansonsten würde die Formsmaske nur noch Refreshs ausführen, wenn der Anwender in der Maske etwas anklickt.

Code-Beispiel

BEGIN
:CONTROL.TI_Payload := get_event_object_property ('EV_Default', Event_Payload);
IF upper (:CONTROL.TI_Payload) = 'ATTACH DEBUG' THEN
DEBUG.Attach;
...
END IF;
END;

Dieses Beispiel zeigt eine Fernsteuerung für ein Remote Debugging. Auf der Datenbank füllt man einfach in einer Queue einen Datensatz ein, der dem Forms-Event EV_DEFAULT zugeordnet ist. Jedesmal, wenn die Payload den Wert ATTACH DEBUG hat, startet man in der Forms Runtime das Debug.

In meinem Download-Bereich gibt es einen Link zum Vortrag.

22. November 2007

DOAG Konferenz 2007

Heute war der letzte Tag der diesjährigen DOAG-Konferenz. Die DOAG (Deutsche Oracle Anwender Gruppe) feierte ihren 20. Geburtstag und feierte diesen gebührend im neuen Conference-Center in Nürnberg. Interessante Vorträge wie z.B. "Oracle 5.0 New Features" gab es :-)

Dies war eine phantastische Präsentation von Dierk Lenz. Er benutzte einfach seine 20 Jahre alten Folien und zeigte uns die neuesten Datenbank-Features aus dem Jahre 1988.

Dies konnte man live miterleben in einer vmWare, in der ein DOS 6.22 mit einem SQL*Forms 2 installiert waren.



In diesem Vortrag waren alleine schon 200 Leute. Einfach nur genial!

1. Oktober 2007

Forms Start-Up (Teil 1)

Dies ist der erste Artikel einer kleinen Serie. Ich zeige an dieser Stelle Methoden und Verbesserungen des Forms-Starts. In fast allen Web-Projekten lernte ich in den vergangenen Jahren Techniken kennen, in denen das Browser-HTML verändert wurde um zum Beispiel das versehentliche Schliessen des Browser-Fensters zu verhindern.

Mein erstes Beispiel kommt von Francois Degrelle. Er gab mir den Tipp mit einem automatisch versteckenden Internet Explorer beim Forms Startup. Der einzige Parameter der geändert werden musste ist der HTMLbodyAttrs in der formsweb.cfg:

HTMLbodyAttrs=onLoad='javascript:self.moveTo(2000,2000)'

Nach dem Forms-Start wandert das Browser-Fenster an die X,Y-Position 2000, 2000. Das ist normalerweise ausserhalb des sichtbaren Bereichs. Dies lässt den Anwender glauben, dass der Browser nun geschlossen ist. Das Icon in der Taskbar ist zwar noch sichtbar, aber der Browser ist nicht zu sehen, selbst wenn der Anwender auf das Icon klickt.

Pro:
- Der Anwender glaubt, dass der Browser sich selber versteckt
- Nur die Forms-Anwendung ist sichtbar (bei separateFrame=True)

Contra:
- Die Task des Browsers ist sichtbar und kann nicht aktiviert werden. Sie kann nur geschlossen werden

Das ist zur Zeit meine Lieblingsvariante

17. September 2007

Bigger. taller. better :-)

Nachdem ich ein wenig am Template-HTML herumgespielt habe fand ich ein paar sehr wichtige Properties für meinen Blog.

Die ehemalige Breite des Blogs war ein echtes Hindernis und sorgte dafür, dass ich Sourcecodes oft kürzen musste, damit sie in eine Zeile passten. Dieses Problem ist jetzt ein für alle Mal gelöst. Nun habe ich 50% mehr Platz für meine Snippets :-)

13. September 2007

Einfaches Logging und Debugging in Forms

Jede Forms-Anwendung braucht eine simple Methode um Fehler wegzuschreiben. Diese Technik kann desweiteren dazu benutzt werden um Forms, Reports und PL/SQL zu debuggen.

Zuerst einmal erstellen wir die Tabelle, Sequence und eine View um die Logging-Information zu speichern:
CREATE TABLE Logging (
  ID                         NUMBER(8,0) NOT NULL,
  SESSION_ID                 NUMBER(8,0),
  INSERT_DATE                DATE NOT NULL,
  TEXT                       VARCHAR2(2000) NOT NULL);

CREATE SEQUENCE Logging_SEQ;

CREATE OR REPLACE VIEW V_Logging_desc 
         (ID, SESSION_ID, INSERT_DATE, TEXT)
AS SELECT ID, SESSION_ID, INSERT_DATE, TEXT
     FROM Logging
ORDER BY SESSION_ID DESC, ID DESC;

Desweiteren brauchen wir ein Package mit den Funktionen und Prozeduren, die für`s Logging benötigt werden.
CREATE OR REPLACE PACKAGE PK_DEBUG IS
  FUNCTION Debug_allowed RETURN BOOLEAN;
  FUNCTION Next_ID       RETURN NUMBER;

  PROCEDURE Disable;
  PROCEDURE Enable;
  PROCEDURE Destroy;
  PROCEDURE Init  (P_Debug_allowed IN BOOLEAN DEFAULT TRUE);
  PROCEDURE Write (P_Text IN VARCHAR2,
                   P_Session_ID IN NUMBER DEFAULT NULL);

  G_Debug_allowed BOOLEAN := TRUE;
  G_Session_ID    NUMBER;
END;
/
CREATE OR REPLACE PACKAGE BODY PK_DEBUG IS
FUNCTION Debug_allowed RETURN BOOLEAN IS
BEGIN
  RETURN (G_Debug_allowed);
END;

FUNCTION Next_ID RETURN NUMBER IS
  V_ID NUMBER;
BEGIN
  SELECT Logging_SEQ.nextval
    INTO V_ID
    FROM DUAL;
  RETURN (V_ID);
END;

PROCEDURE Disable IS
BEGIN
  G_Debug_allowed := FALSE;
END;

PROCEDURE Enable IS
BEGIN
  G_Debug_allowed := TRUE;
END;

PROCEDURE Destroy IS
BEGIN
  Write ('----------------------stopp '
    || to_char (G_Session_ID) || '--');
  G_Session_ID := NULL;
END;

PROCEDURE Init (
  P_Debug_allowed IN BOOLEAN DEFAULT TRUE) IS
BEGIN
  G_Debug_allowed := P_Debug_allowed;
  G_Session_ID := Next_ID;
  Write ('--start ' || to_char (G_Session_ID)
    || '----------------------');
END;

PROCEDURE Write (
  P_Text       IN VARCHAR2,
  P_Session_ID IN NUMBER DEFAULT NULL) IS
  PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
  IF Debug_allowed THEN
    IF G_Session_ID IS NULL THEN
      Init;
    END IF;
    INSERT INTO Logging (ID,
      Session_ID, Insert_Date, Text)
    VALUES (Next_ID,
      NVL (P_Session_ID, G_Session_ID),
      Sysdate, P_Text);
    COMMIT;
  END IF;
END;
END;
/

Das Debugging wird mit INIT gestartet und endet mit DESTROY. Fehlermeldungen werden in die Tabelle festgeschrieben mit WRITE. Zum Beispiel:
pk_Debug.Write ('Hello World - ' || V_Test);

Teile des Debuggings können deaktiviert werden mit DISABLE, so dass von dieser Zeile Code an keine Logging-Informationen mehr geschrieben werden, bis man ENABLE startet.

Die View V_Logging_desc zeigt die Logging-Daten, gruppiert nach der neuesten Session-ID.
ID Session Insert-Date     Text
============================================
24    21   10.09.-12:38:48 -------stopp 21--
23    21   10.09.-12:38:48 Hello World - 42
22    21   10.09.-12:38:48 --start 21-------


Viel Spass damit
Gerd

31. August 2007

San Francisco Impressionen

Hier sind einige Fotos von San Francisco, aufgenommen in der Zeit nach dem Test-Event
Lombard Street Richtung Coit Tower

17 Miles Drive

Muir Woods

Der Beweis: Second Life existiert doch in der Realität

Steinkunst in Sausalito

Die Golden Gate