Lade...
 

Nachrichten

Events/Messages

Events sind ein wesentliches Konstrukt zur Ablaufsteuerung innerhalb der ClassiX-Architektur. Sie dienen

  • der Kommunikation zwischen der Umgebung (Oberfläche, WebService, TAPI, ...) und ClassiX
  • als Schnittstelle zwischen ClassiX-Modulen
  • als Kommunikationsweg zwischen den ClassiX Modell-Klassen und InstantView (z.B. bei der Formelauswertung)
  • als Abstraktionsmechanismus für den Entwickler
  • als Interface für Fremdanwendungen (z.B. CORBA)

Benutzerdefinierte Messages und vordefinierte System-Events

Es gibt einerseits sogenannte System-Events, die dem System bereits bekannt sind. Mit diesen wird über vordefinierte Ereignisse informiert (z.B. Button wurde gedrückt: Message SELECT wird ausgelöst).

Zum anderen kann der InstantView-Entwickler eigene Events mit dem InstantView-Statement Message deklarieren.

Abgesehen davon, dass System-Events nicht mehr deklariert werden müssen und vom System zu bestimmten Ereignissen gesendet werden, gibt es keinen Unterschied zwischen benutzerdefinierten Events und System-Events: Beide werden gleich empfangen und können gleich versendet werden. Daher wird zwischen diesen häufig auch nicht klar unterschieden.

Empfangen eines Events

Ein Event kann im InstantView-Code entweder auf der Ebene eines Moduls oder auf der Ebene eines Fensters (oder Widgets) behandelt werden. Hierdurch kann der Kontext eines Events festgelegt werden: Eine Behandlung des SELECT-Events in einem Button wird genau beim Klicken dieses einen Buttons ausgeführt.

209330 Findet das ClassiX-System in einem Widget das auszulösende Event nicht, so werden die übergeordneten Widgets nach diesem Event durchsucht und das Event ggf. an diesen ausgelöst. So ist es bspw. möglich, ein Event, wie die F2-Taste für alle Widgets innerhalb einer Gruppe abzufangen.

Davon ausgenommen sind die Events CURRENT, NON_CURRENT und INITIALIZE

Motivation: Diese Events werden spezifisch an einzelne Widgets gesendet.
Beispiel: Wechselt der Fokus von im nachfolgenden Beispiel Debit nach Credit, empfängt Debit ein NON_CURRENT und Credit ein CURRENT. Die umschließende Gruppe AccountingGrp bleibt währenddessen durchweg aktiv. Ein NON_CURRENT in der Gruppe darf also erst dann ausgelöst werden, wenn keines der Widgets der Gruppe mehr aktiv ist. Wurde hingegen in Debit F2 gedrückt, wurde implizit auch in der Gruppe F2 gedrückt. Dies in der Gruppe abzufangen ist also sinnvoll und man verliert keine Spezifizität.

Group(AccountingGrp, 0, 0, 340, 102) [ F2: ClearWindow ] { Numeric(Debit, 7, 14, 208) Numeric(Credit, 224, 14, 50) }

Bei vom System verschickten Events wird außerdem der Kontext, unter dem der nachfolgende InstantView-Code abläuft, entsprechend des "Empfängers" (also Fenster, Widget oder Modul) gesetzt. Dieser Kontext erlaubt es, Parameter für bestimmte Befehle wegzulassen (bspw. der Fenstername beim Befehl FillWindow).

Versenden von Messages

Messages sind neben Providern das einzige Mittel, mit dem aus einem Modul A heraus Programmcode in einem anderen Modul B aktiviert werden kann (Kapselung im Sinn der Objektorientierung).

Eine Message wird mit SendMsg, PostMsg, SendMsg(..., SUPER) oder SendMsg(..., DIRECT) zu anderen Window-Objekten und Modulen geschickt, und dies triggert die Ausführung der mit der Message verbundenen Anweisungen.

Reihenfolgeregeln

Wurde per SendMsg eine Message versendet, gelten folgende Regeln über die Reihenfolge der Empfänger:

  • Empfangen mehrere Module die gleiche Message, ist die Reihenfolge (zwischen den Modulen) zufällig.
  • Module kommen vor Window-Objekten: Reagieren ein Modul und Windowobjekte dieses Moduls auf die gleiche Message, wird immer zuerst die Befehlsfolge beim Modul ausgeführt.
  • Für Window-Objekte auf gleicher Ebene (der Parent-Child-Hierarchie) ist die Reihenfolge zufällig.
  • Es ist garantiert, dass ein übergeordnetes Window-Objekt eine Message immer früher empfängt als die ihm untergeordneten Window-Objekte.

Anmelden von Messages

Von InstantView aus können also beide Arten von Events auf dieselbe Art und Weise empfangen und gesendet werden. Das ClassiX-System selbst verschickt zunächst nur vordefinierte System-Events.

In einigen Fällen ist es der Code-Organisation wegen sinnvoll, beliebige Events explizit am System anzumelden. Hierzu werden dem System über bestimmte Methoden diese Events mitgeteilt, die dann im entsprechenden Fall aufgerufen werden. Bspw. SystemObject::RegisterAttentionNotification, bei dem das Event ausgelöst wird, wenn eine Attention-Benachrichtigung geöffnet werden soll.

Webservice-Messages

Eine Ausnahme zu dieser Aufteilung stellen die Webservice-Messages dar. Diese sind benutzerdefinierte Messages, müssen also deklariert werden, werden vom System jedoch über eine Namenskonvention beim Aufruf eines Endpunktes im Web aufgerufen. So führt ein GET-Request auf die Adresse /fetch_data dazu, dass die Message FETCH_DATA_GET ausgeführt wird.

Hierzu siehe CX_WEB_SERVICE_MANAGER.

Security

Für die Sicherheit können Messages über ein CX_MESSAGE_SECURITY-Objekt unterbunden werden. Siehe hierzu das Sicherheitskonzept von ClassiX.

Beispiel

Im Folgenden wollen wir den Ablauf beim Senden einer Message X genau betrachten.
Das Window in Modul A ist bereits geöffnet; wir können den Button "push me" drücken.

Zuerst eine Definition: das aktuelle Modul. Das Run-Time-System des InstantView®-Interpreters betrachtet genau eines der Module als aktuelles Modul - es ist immer das Modul, dessen Programcode gerade ausgeführt wird.

Das Drücken des Buttons macht Modul A zum aktuellen Modul. War vorher ein anderes Modul aktuell, wird CURRENT für Modul A getriggert; d.h. die Anweisungsfolge Current_at_A wird ausgeführt, noch bevor die mit dem Button verbundene Anweisung SendMsg(X) zur Ausführung kommt.
War A bereits aktuelles Modul, wird nur SendMsg(X) ausgeführt.

msgdocA.bmp (1430430 Byte)

 

Auf das Empfängermodul wird nur durch eine Extern-Anweisung verwiesen, d.h. es ist - wenn wir den Button zum ersten Mal drücken - noch nicht geladen.
Der Extern-Anweisung im Sourcecode entspricht bereits eine interne Repräsentation eines Moduls B - eine reduzierte Variante, die lediglich Message X empfangen kann, um dann als Reaktion das "richtige" Modul B zu laden.
Bevor Message X das Laden aktiviert, verliert Modul A seinen Status als aktuelles Modul und die Anweisungen NonCurrent_at_A werden ausgeführt. Dann wird der Sourcecode aus dem File B.MOD geladen (mit Tests, die sicherstellen, dass B.MOD auch das Modul B enthält). Wenn File B.MOD den Source-Code weiterer Module enthielte, würden diese jetzt auch mitgeladen.
Jetzt kann Message X vom "echten" Modul B empfangen werden.

MsgDocB.bmp (1395054 Byte)

Eine Message zu empfangen heißt nichts anderes, als alle an die Message gebundenen Anweisungen ausführen - und dazu muss zuerst Modul B zum aktuellen Modul werden. Deshalb wird zunächst CURRENT getriggert (Current_at_B aufgerufen). Weil dies der erste Aufruf für Modul B ist, wird dann INITIALIZE getriggert - also DoSomeInitializationStuff ausgeführt. Anschließend kommt endlich Receive_X_at_B an die Reihe - das eigentliche Ziel der Message X.
Receive_X_at_w1 wird nicht ausgeführt - ein Windowobjekt kann nur dann eine Message empfangen, wenn das Window geöffnet ist.

Drücken wir den Button zum zweiten Mal, entfällt das Laden des Moduls B und auch INITIALIZE wird nicht mehr getriggert.
Nehmen wir zusätzlich an, dass irgendjemand (z.B. mit SendMsg(Y)) das Window im Modul B geöffnet hat, läuft SendMsg(X) jetzt wie folgt ab:

MsgDocC.bmp (1400310 Byte)

In der Abbildung fehlt Nummer 1, denn zuerst wird NON_CURRENT im Modul A getriggert.