Erweiterte Programmierung


Eine ACComponent besitzt die Eigenschaft InitState vom Typ gip.core.datamodel.ACInitState die eine Enumeration darstellt. Die InitState gibt an in welcher Lebensphase sich die ACComponent befindet. Welche Zustände diese während Ihrer Lebenszeit durchlaufen kann ist im folgenden Bild dargestellt:

 

Im Allgemeinen kann man den Lebenszyklus in drei Arten von Zyklen unterteilen:

  • Standardzyklus (Blau hervorgehoben).
  • Poolingzyklus (Orange hervorgehoben)
  • Aktualisierungszyklus (Grün hervorgehoben)

Initialisierungsphasen
Die Erzeugung der ACComponents erfolgt automatisch durch die Laufzeitumgebung per Reflektion. Die Informationen über die Komponente stellt die Datenbank zu Verfügung. Das sind z.B.:

  • Eigenschaften,
  • Defaultwerte,
  • Konfigurationsdaten,
  • Variablenbindungen
  • und Skriptmethoden.

Während dieser Phase durchläuft die Komponente den blau hervorgehobenen Zustandsgraphen von Contructing bis Initialized.

Terminierungsphasen

Wird eine Intanz nicht mehr benötigt oder iPlus beendet, dann wird der zweite Teil des blau hervorgehobenen Zustandsgraphen durchlaufen (Destructing->Destructed).


Während der Laufzeit müssen oft Instanzen desselben Typs instanziiert und entladen werden. Der im Standardzyklus beschriebene Initialisierungsvorgang ist hierfür relativ aufwändig und kostet wertvolle CPU-Zeit. Eine Optimierung ist durch poolen von nicht mehr benötigten Instanzen möglich. Liegt bereits eine Instanz des Typs im Pool vor, kann die iPlus-Runtime die Instanz aus dem Pool abrufen und wiederverwenden. Den Pooling-Mechanismus und das dafür nötige Caching übernimmt die iPlus-Runtime für Sie.

Damit die iPlus-Runtime weiß ob Ihre eigene ACComponent-Klasse poolfähig ist, müssen Sie in Ihrer Implementierung die virtuelle Eigenschaft IsPoolable überschreiben und als Rückgabewert true zurückgeben.

Disposingphasen
Wird eine Instanz nicht mehr benötigt dann wird der erste Teil des orange hervorgehobenen Zustandsgraphen durchlaufen (DisposingToPool->DisposedToPool).

Recyclingphasen
Wird eine neue Instanz benötigt dann überprüft die iPlusRuntime im Pool ob für den gesuchten Typ bereits eine Instanz existiert.

  • Falls diese existiert, dann wird diese wiederaufbereitet und der zweite Teil des orange hervorgehobenen Zustandsgraphen durchlaufen (RecyclingFromPool->RecycledFromPool->Initialized).
  • Falls diese nicht existiert, dann wird eine neue Instanz im Standardzyklus erzeugt.

Während der Inbetriebnahme kann es vorkommen dass:

  • Korrekturen in Skripten und Workflows durchgeführt,
  • Zusätzliche Eigenschaften hinzugefügt,
  • oder neue Routen hinzugefügt werden.

Bei solchen Änderungen ist ein Neustart der entsprechenden Variobatch-Dienste erforderlich. Häufig bietet sich kaum ein Zeitfenster dafür, da gleichzeitig die Produktionsanlage läuft. Um einen Neustart zu vermeiden erfolgt eine Reinitialisierung der betroffenen Komponenten durch einen Aktualisierungszyklus.

Achtung: Diese Funktionalität ist noch in der Betaphase, eine Nutzung ist noch nicht möglich!


Die Erzeugung einer ACComponent erfolgt über eine der StartACComponent -Methoden per Reflection. Aus diesem Grund müssen alle Konstruktoren die gleiche Signatur vorweisen. Die Signatur muss wie folgt aufgebaut sein:


Parameter:

acTypeACClassDer iPlus-Datentyp (Tabelle ACClass)
contentIACObjectEntität welche die Komponente kapselt. Bei Workflowinstanzen (Ableitungen der Klasse PWBase) handelt es sich um eine Instanz von ACClassWF beim Neuladen eines Workflows. Sonst ist es eine Referenz auf ACClassTask welche die persistierte Form einer ACComponent in der Datenbank repräsentiert.
parentACObjectIACObjectReferenz auf die Eltern-ACComponent.
parameterACValueListParameterliste die individuelle bzw. projektbezogene Informationen liefert. Die Parameterliste dient als Ersatz dafür, dass die Konstruktor-Signatur immer gleich sein muss.
acIdentiferstringVorgegebener ACIdentifier der bei programmgesteuerter Instanziierung gesetzt bzw. beim Aufruf der Start - Methoden übergeben werden kann. Ist in der Regel leer, weil die iPlusRuntime den ACIdentifer und die fortlaufende Instanznummer vergibt.

 

Grundsätzlich müssen Sie den Konstruktor seiner Basisklasse (base) immer aufrufen, da über die Ableitungshierarchie zuletzt der Konstruktor aus der ACComponent-Klasse aufgerufen wird. Der Konstruktor führt dann folgende Aktionen aus:

  1. Zustandsänderung der InitState-Eigenschaft auf ACInitState.Constructing.
  2. Aufruf der virtuellen Construct-Methode, die Sie nicht überschreiben brauchen. Wenn Sie sie überschreiben sollten, dann müssen unbedingt den base-Aufruf machen. In der Construct-Methode wird die ACClassTask-Instanz geladen, die nötig ist um Persistente Daten für Eigenschaften, Asynchrone Aufrufe, Event-Abbonierungen, Konfigurationsdaten lesen und somit den Zustand bei einem iPlus-Neustart wiederherstellen / rekonstruieren zu können.
  3. Zustandsänderung der InitState-Eigenschaft auf ACInitState.Constructed.

Beispiel zum Überschreiben der Construct-Methode:

 

 


Der Aufruf der ACInit-Methode erfolgt direkt nach der Konstruktion. Die ACInit-Methode können Sie ebenfalls überschreiben. Beim Hochfahren oder dynamischen Nachladen von ACComponent-Bäumen wie z.B. Laden eines Workflows, durchlaufen die Bäume den "Depth-First" + "Pre-Order" - Algorithmus. Das bedeutet dass immer zuerst die Erzeugung von Kind-ACComponents in Tiefe erfolgt und danach die nächste ACComponent auf der selben Rekursionstiefe.


Die Ausführung des Algorithmus findet in der ACInit-Methode der ACComponent-Klasse statt. Deswegen müssen Sie immer den Basis-Aufruf durchführen! Das bedeutet, sobald Sie Ihre Initialisierungslogik nach dem Basis-Aufruf durchführen, wissen Sie dass die Kind-Komponenten erzeugt wurden und diese sich im Initialisierungszustand befinden.

 
Die Basis-ACInit-Methode der ACComponent führt folgende Aufgaben durch:

  • Initialisiert alle Eigenschaften mit ihren Default-/oder persistierten Werten,
  • Führt Eigenschaftsbindungen zwischen Source- und Target-Eigenschaften durch,
  • Initialisiert Point-Eigenschaften und deren Verbindungen (z.B. Routen),
  • Kompiliert Skript-Methoden die über die Entwicklungsumgebung hinzugefügt worden sind.

Nach der Initialisierung aller neuen Instanzen durch die Vorinitialisierungsphase, startet die Nachinitialisierungphase. Nach Durchlauf des "Depth-First" + "Post-Order" - Algorithmus erfolgt der Aufruf der ACPostInit-Methode. Der Durchlauf des Elternelements erfolgt dann, wenn es keine Kindelemente auf der selben Rekursionstiefe mehr gibt, beginnend mit der niedrigsten Rekursionstiefe:


Die ACPostInit-Methode können Sie ebenfalls überschreiben, um restliche Initialisierungslogiken zu programmieren. Meist ist das Programmcode, der mit Referenzen auf andere ACComponent-Instanzen arbeitet und der nur dann funktionieren kann wenn sichergestellt ist dass alle benötigen Instanzen im Speicher auch vorhanden sind (bzw. duch die ACInit-Phase zuvor erzeugt worden sind).

In der Basis-ACPostInit-Methode der ACComponent werden noch die restlichen Eigenschaftsbindungen durchgeführt, so dass dann die Target-Eigenschaften den Wert der gebundenen Source-Eigenschaft besitzen. Deswegen müssen sie beim überschreiben dieser Methode den base-Aufruf auf jeden Fall durchführen. Daher ist es empfehlenswert immer zuerst den Basis-Aufruf vor Ihrer eigenen Logik einzufügen, damit Sie mit gültigen Eigenschaftswerten arbeiten:

 
Nach Aufruf der base.ACPostInit()-Methode erhält die Instanz den Zustand Initialized.


Das Beenden einer ACComponent-Instanz wird durch die Methode StopACComponent() eingeleitet. Die Methode ACPreDeInit informiert die betroffenen Kind-Instanzen vorab dass eine Deinitialisierung stattfindet. Der Aufruf der ACPreDeInit erfolgt nach dem "Depth-First" + "Pre-Order" - Algorithmus

 


Nach der Benachrichtigung aller betroffener Instanzen durch den Aufruf der ACPreDeInit-Methode startet die eigentliche Deinitialisierung. Diese durchläuft die Instanzen nach dem "Depth-First" + "Post-Order" - Algorithmus und ruft die ACDeInit-Methode auf. Diese sollten Sie überschreiben, damit Sie u.a. Referenzen freigeben oder sich von Events abmelden.


Sie dürfen nicht vergessen den Basis-Aufruf durchzuführen!
Die Basis-ACDeInit-Methode der ACComponent führt einige Funktionen aus um den Garbage-Collector wieder frei zuräumen. Vor Aufruf dieser Funktion wird zuerst:

  • der Zustand auf Destructing gesetzt.
  • Falls die Instanz "poolbar" ist - also IsPoolable == true ist -, dann erhält sie stattdessen den Status DisposingToPool.

Am Ende der Deinitialisierung wird:

  • der Zustand auf Destructed gesetzt,
  • oder bei einer "poolbaren" Instanz auf DisposedToPool. In diesem Fall wird die Instanz nicht vom .NET-Garbage-Collector aufgeräumt sondern sie wartet in einem Pool, dass sie wieder reaktiviert wird.

  1. Damit Ihre von ACComponent abgeleitete Klasse poolfähig ist, müssen Sie in der überschriebenen ACDeInit-Methode alle Ihre Klassenmitglieder zurücksetzen und Referenzen entfernen. Dies entspricht dem Zustand, der normalerweise nach dem Ausführen des Konstruktors vorliegt.
  2. Überschreiben Sie die Eigenschaft IsPoolable und geben true zurück.
  3. Optional: Testen Sie Ihre Klasse, indem Sie im Debugger des MS Visual Studio eine Objekt-ID für die Instanz vergeben, die sich später im Pool befindet. Wenn dann die iPlus Laufzeit die Instanz erneut zurückholt, wird zuerst die Methode Recycle() aufgerufen, die Sie überschreiben können. Dort vergleichen Sie dann mittels der Objekt-ID, ob es sich um dasselbe Objekt handelt. Alternativ können Sie die Methode Construct überschreiben und dort den Objekt-ID-Vergleich machen. Überprüfen Sie das Verhalten bis zum ACPostInit-Aufruf über die Objekt-ID. Wenn die Komponente sich genauso verhält wie bei einer Neugenerierung können Sie, dann die überschriebenen Methoden entfernen, falls Sie diese nicht mehr benötigen sollten.

Reihenfolge des Recyclings:
Die Wiederherstellung einer Instanz aus dem Pool findet im gleichen Zyklus statt wie er in der Vor- und Nachinitialisierungsphase beschrieben ist (Baum Traversierung). Der Unterschied besteht lediglich darin, dass an Stelle des Konstruktors ein Aufruf der Recycle-Methode stattfindet.

  1. Die Basis-Implementierung der Recycle-Methode setzt zuerst den Zustand auf RecyclingFromPool.
  2. Danach wird die Construct-Methode aufgerufen und der Zustand auf RecycledFromPool gesetzt.
  3. Zuletzt erfolgt wie gewohnt die ACInit() und ACPostInit()-Aufrufe. In den Init-Methoden können Sie nun über die InitState-Eigenschaft unterscheiden ob es sich um eine neu erzeugte Instanz (Initializing) oder eine wiederaufbereitete Instanz (RecycledFromPool) handelt.

 


Initialisierungs-Queues

Manchmal sind die zuvor beschriebenen Initialisierungs-Methoden nicht der richtige Weg, um individuelle Logik während der Initialisierungs- oder Deinitiliaisierungsphasen einzufügen. Oft möchte man z.B. bei einem Callback oder dem Aufruf einer anderen Methode seine spezielle De-/Initialisierungs-Logik einbauen. Dafür verwenden Sie die Methoden AddToPostInitQueue (Action action) und AddToPostDeInitQueue (Action action). Übergeben Sie als Parameter einen Delegaten in dem Ihre Logik enthalten ist. Damit wird ein Eintrag in eine Queue gemacht. Diese Queue wird immer dann abgearbeitet nachdem entweder alle ACPostInit()- bzw. ACDeInit()-Aufrufe durchgeführt worden sind.