Änderungsprotokoll

Wertauflöser - Kurzfassung

Zweck: Erzeugt ein Änderungsprotokoll, das die Unterschiede zwischen einem als Eingabewert übergebenen Objekt und einem parametrierten Vergleichsobjekt ermittelt und in strukturierter Form - als "Änderungsprotokoll" - ausgibt.

images/download/attachments/177910326/image-2024-9-10_10-46-16-version-1-modificationdate-1725957976133-api-v2.png


WICHTIG◄ Dieser Wertauflöser ist in einem Client Workflow nicht verfügbar!


Der Änderungsprotokoll-Wertauflöser erzeugt ein Änderungsprotokoll, das die Unterschiede zwischen einem als Eingabewert übergebenen Objekt und einem per Parameter Vergleichswert definierten Vergleichsobjekt ermittelt und in strukturierter Form - als "Änderungsprotokoll" - ausgibt.

Der Typ des Rückgabewerts hängt dabei vom Typ des Eingabewerts ab:

Eingabewert-Typ

Beispiele

Rückgabewert-Typ

Spezifische Felder

Simpler Wert

Textwerte, Zahlenwerte, Aufzählungswerte, usw.

"Änderungsprotokoll > Einfacher Wert" (SimpleValueChangeLog)

fromValue, toValue

Collection

Liste, Eindeutige Liste, usw.

"Änderungsprotokoll > Collection" (CollectionChangeLog)

fromLength, toLength

Entität

Geschäftsobjekt, Eigene Typdefinitionen, Position, Konfiguration,
Attribut-Implementierung (s. a. nachfolgende Tabelle), usw.

"Änderungsprotokoll > Entität" (EntityChangeLog)

fromId, toId

andere Objekte

Client-Objekt, Datumswert, Datumsbereich, Attribut (s. a. nachfolgende Tabelle), usw.

"Änderungsprotokoll > Objekt" (ObjectChangeLog)

fromClass, toClass

WICHTIG◄ Technisch bietet jeder Änderungsprotokoll-Typ die Felder fromIndex und toIndex an, die sich auf die Reihenfolgenposition innerhalb einer Collection beziehen. Diese Felder enthalten den Wert -1, falls der Index (mangels Bezug zu einer Collection) nicht anwendbar ist.

HINWEIS◄ Änderungen an Attributen einer Entität können auf unterschiedlichen Wegen ermittelt werden. Der Rückgabewert-Typ variiert dabei:

Eingabewert für das Änderungsprotokoll

Eingabewert-Typ

Rückgabewert-Typ

Spezifische Felder

Attributbesitzer Übergeordnete Entität des Attributs
z. B. ein Geschäftsobjekt oder auch eine Position

Entität

"Änderungsprotokoll > Entität" (EntityChangeLog) für den Attributbesitzer, das für jedes geänderte Attribut einen der folgenden Änderungsprotokoll-Typen beinhaltet:

  • "Änderungsprotokoll > Singuläres Attribut" (SingularAttributeChangeLog)

  • "Änderungsprotokoll > Plurales Attribut" (PluralAttributeChangeLog)

  • "Änderungsprotokoll > Typisiertes Attribut" (TypedAttributeChangeLog)

attributeType

Attribut-Implementierung
z. B. Typ "Bestellung > Textattribut"

Entität

"Änderungsprotokoll > Entität" (EntityChangeLog) für eine Instanz der Attribut-Implementierung als selbstständige Entität, die das eigentliche Attribut als Wert (s. nächste Zeile) im value-Feld beinhaltet.

keine bzgl. Attribut

Attribut
z. B. Typ "Textattribut"

Objekt

"Änderungsprotokoll > Objekt" (ObjectChangeLog) für das Attribut, das Änderungsprotokolle für andere Typen (s. o.) beinhaltet, um die eigentlichen Änderungen zu beschreiben.

keine bzgl. Attribut

Vergleichslogik:

  • Werden keine Unterschiede zwischen dem Eingabewert und dem Vergleichsobjekt festgestellt, lautet der Rückgabewert "Kein Wert" ($null).

  • Falls kein Vergleichswert konfiguriert ist (Standard) und zur Laufzeit eine Entität als Eingabewert vorliegt, wird auf deren Original-Objekt (also den serverseitigen Stand für die Entität) als Vergleichsobjekt zurückgegriffen. Dies ist der typische Anwendungsfall für den Wertauflöser.

    • Die Option Entitätsattribute ignorieren wirkt nur in diesem Anwendungsfall. Sie regelt, ob Veränderungen an den Entitätsattributen ("Besitzer", "Erstellt von", "Erstelldatum", usw.) beim Vergleich ignoriert werden (Standard) oder berücksichtigt.

    • Beinhaltet die Entität im Eingabewert andere Entitäten (z. B. Positionen oder Attribute), wird für diese - ggf. rekursiv - ein Änderungsprotokoll erzeugt, das zusammen mit anderen "Änderungen" als Bestandteil des übergeordneten Änderungsprotokolls erscheint.

  • Handelt es sich beim Eingabewert nicht um eine Entität, sondern z. B. um einen simplen Wert, eine Liste oder ein "Client-Objekt", dann sollte der Vergleichswert explizit konfiguriert sein, sonst gilt er Eingabewert auch als Vergleichsobjekt und der Wertauflöser gibt "Kein Wert" ($null) zurück.

    • Falls als Eingabewert eine Liste von Entitäten vorliegt, werden diese nicht automatisch mit dem jeweiligen Original-Objekt verglichen, wenn kein Vergleichswert konfiguriert ist. Allerdings kann das bei Bedarf durch eine explizite Konfiguration für den Vergleichswert erreicht werden (s. Besonderer Anwendungsfall im Abschnitt "Beispiele").

  • Die Option Änderungen auch für gelöschte und erstellte Werte erzeugen steuert, wieviel Information das Änderungsprotokoll über Objekte bereitstellt, die im Kontext des Vergleichs als erzeugt oder gelöscht eingestuft werden:

    • Für erstellte oder gelöschte Werte erscheint per Standard (Option abgewählt) nur ein minimaler Informationsumfang.

    • Wird die Option ausgewählt, gibt das Änderungsprotokoll mehr Daten des betreffenden Objekts aus:

      • Ein als "gelöscht" eingestuftes Objekt werden "zum Abschied" komplett alle Details wiedergegeben.

      • Für ein als "erstellt" eingestuftes Objekt werden dessen Daten durch ein "Änderungsprotokoll" mit "Kein Wert" ($null) als Vergleichsobjekt wiedergegeben.

►WICHTIG◄ Welche Art von "Änderungen" beschreibt das Änderungsprotokoll?


Beim Änderungsprotokoll handelt es sich nicht um ein chronologisches Verzeichnis der an einem konkreten Objekt tatsächlich vorgenommenen Manipulationen. Tatsächlich werden zwei Objekte (der Eingabewert und das Vergleichsobjekt) miteinander verglichen, bei denen es sich um unterschiedliche Versionen desselben Objekts handeln kann aber nicht muss. Allgemein formuliert beschreibt das "Änderungsprotokoll" eine oder mehrere "Änderungen", durch die das Vergleichsobjekt in den Eingabewert überführt werden könnte. Das Änderungsprotokoll beschreibt also eine mögliche Transformation vom Vergleichsobjekt zum Eingabewert, ohne den Anspruch zu erheben, dass die beschriebenen "Änderungen" überhaupt und, wenn genau so stattgefunden haben.

Beispiele zur Veranschaulichung der Bewertungslogik im Änderungsprotokoll:

  • Im Eingabewert liegt ein volatiler Datenstand für eine beliebige Entität vor, die über ein Feld "Name" (name) verfügt. Der serverseitige Datenstand (s. Original-Objekt) weist für das name-Feld den Text "foo" aus. Im volatile Datenstand wurde im Verlauf der aktuellen Transaktion der Text "bar" als "Name" zugewiesen.

    • Solange in der Transaktion keine anderen Änderungen an der Entität vorgenommen wurden, gilt die Entität damit effektiv als geändert und der Änderungsprotokoll-Wertauflöser weist für das Feld "Name" die Wertänderung von "foo" nach "bar" aus.

  • Als Erweiterung zum vorherigen Szenario wird der "Name" der Entität nach der Änderung von "foo" nach "bar" wieder zurück auf den Originalwert, also "foo", geändert.

    • Solange in der Transaktion keine anderen Änderungen an der Entität vorgenommen wurden, gilt die Entität damit als effektiv unverändert und der Änderungsprotokoll-Wertauflöser gibt "Kein Wert" ($null) zurück.

  • Im Eingabewert liegt eine Liste mit drei Textzeichen (JSON-Abbild: ["O","X","O"]) vor, der als Vergleichswert eine Liste mit dem JSON-Abbild ["O","O","O"] gegenübergestellt wird.

    • Intuitiv würde man vielleicht erwarten, dass der zweite Wert der Liste als "von "O" auf "X" verändert" gewertet wird. Dagegen weist der Änderungsprotokoll-Wertauflöser sinngemäß folgende "Änderungen" aus:

      • Das "O" an der dritten Listenposition im Vergleichswert wurde in ein "X" umgewandelt und an die zweite Position verschoben.

      • Das "O" an der zweiten Listenposition wurde an die dritte Listenposition verschoben.

    • Das Endergebnis dieser Operationen entspricht ebenso dem Eingabewert wie die oben als "intuitiv" beschriebene Änderung der zweiten Listenposition. Warum so kompliziert? Der Wertauflöser ermittelt die Änderungen an einer Liste nach einer Systematik, die die Liste im Eingabewert zuerst nach aus dem Vergleichsobjekt bereits "bekannten" Einträgen durchsucht und erst danach nicht zugeordnete Zielwerte "hinzugefügt" oder "geändert" bewertet. Die für den Menschen intuitive "Nebenbedingung", durch eine minimale Anzahl von "Änderungen" vom Vergleichswert zum Eingabewert zu gelangen, spielt für diesen Algorithmus keine Rolle.

Beispiel eines Änderungsprotokolls:

<?xml version="1.0" encoding="UTF-8"?>
<core:EntityChangeLog [...] fromIndex="-1" toIndex="-1" changeType="MODIFIED" fromClass="shp:Shipment" toClass="shp:Shipment" fromId="51" toId="51">
<changeLogs>
<core:TypedAttributeChangeLog name="attributes" fromIndex="-1" toIndex="-1" changeType="MODIFIED" fromClass="shp:ShipmentText" toClass="shp:ShipmentText" fromId="1" toId="1" attributeType="base:TextAttribute" type="base:TextType#DESCRIPTION">
<changeLogs>
<core:ObjectChangeLog name="value" fromIndex="-1" toIndex="-1" changeType="MODIFIED" fromClass="base:TextAttribute" toClass="base:TextAttribute">
<changeLogs>
<core:SimpleValueChangeLog name="textValue" fromIndex="-1" toIndex="-1">
<fromValue xsi:type="xsd:string">Beschreibung (original)</fromValue>
<toValue xsi:type="xsd:string">Beschreibung (geändert)</toValue>
</core:SimpleValueChangeLog>
</changeLogs>
</core:ObjectChangeLog>
</changeLogs>
</core:TypedAttributeChangeLog>
</changeLogs>
</core:EntityChangeLog>
  • Der Rückgabewert vom Typ "Änderungsprotokoll > Entität" (EntityChangeLog) definiert über das Feld toClass dass eine Sendung (s. Sendungen) als Eingabewert übergeben wurde und verweist auf deren ID (toId) mit dem Wert 51.

    • Da die Felder fromClass und fromId keine vom Eingabewert abweichenden Daten angeben, betrifft der Vergleich offenbar zwei Datenstände für die Sendung mit der ID 51. Typischerweise dient dabei das Original-Objekt als Vergleichsobjekt, weil kein Vergleichswert konfiguriert wurde.

  • Das Listenfeld changeLogs enthält im Beispiel genau ein Element vom Typ "Änderungsprotokoll > Typisiertes Attribut" (TypedAttributeChangeLog), dessen Feld changeType den "Änderungstyp" als MODIFIED (geändert) benennt.

    • An den Feldern fromClass und toClass ist erkennbar, dass das TypedAttributeChangeLog ein typisiertes Textattribut - genauer: dessen Implementierung - für eine Sendung (ShipmentText) betrifft.

    • Das attributeType-Feld verweist auf den als Wert verwendeten Attributtyp (TextAttribute) und das Feld type identifiziert den als Wert aus der Dynamischen Aufzählung Texttyp (TextType) definierten Subtyp "Beschreibung" (DESCRIPTION).

  • Das Listenfeld changeLogs innerhalb des TypedAttributeChangeLog-Elements enthält ein "Änderungsprotokoll > Objekt" (ObjectChangeLog), das das Attribut (TextAttribute) als geändert (MODIFIED) klassifiziert.

    • Das name-Feld verweist hier auf den Namen des value-Felds im Datenmodell des übergeordneten ShipmentText-Objekts.

  • Das Listenfeld changeLogs innerhalb des ObjectChangeLogs enthält ein "Änderungsprotokoll > Einfacher Wert" (SimpleValueChangeLog) für das textValue-Feld (s. name) im TextAttribute-Objekt.

    • Das SimpleValueChangeLog dokumentiert die Änderung des Textwerts von "Beschreibung (original)" (fromValue) nach "Beschreibung (geändert)" (toValue)


images/s/-95e2zf/9012/8yg2g7/_/images/icons/emoticons/warning.svg ACHTUNGimages/s/-95e2zf/9012/8yg2g7/_/images/icons/emoticons/warning.svg Wie das Beispiel zeigt, erzeugt bereits eine simple Textwert-Änderung innerhalb eines bereits existierenden typisierten Attributs einer Sendung einen erheblichen Informationsumfang im Rückgabewert des Änderungsprotokoll-Wertauflösers. Beim Einsatz des Wertauflösers ist zu bedenken, dass multiple Änderungen innerhalb von komplexeren Objekten (etwa ein Geschäftsobjekt mit Positionshierarchie und Attributen auf allen Ebenen) einen erheblichen "Rechenaufwand" bedingen und entsprechende Datenmengen erzeugen können. Oft soll ein umfangreiches Änderungsprotokoll im Anschluss auch noch spezifisch ausgewertet werden, um relevante Informationen zu erhalten oder in leichter "lesbarer" Form aufzubereiten.


Konfiguration

Option "Entitätsattribute ignorieren"

Die Option Entitätsattribute ignorieren ist per Standard ausgewählt.

Beim Vergleichen von "Entitätsdaten" werden die sogenannten Entitätsattribute dann nicht ausgewertet.

Beispiel: Als Eingabewert liegen die volatilen Daten einer beliebigen Entität vor, die (ohne Konfiguration für den Vergleichswert) mit dem serverseitigen Datenstand abgeglichen werden sollen. In der rechts oben abgebildeten Standardkonfiguration für den Änderungsprotokoll-Wertauflöser ist die Option Entitätsattribute ignorieren ausgewählt.

Falls die volatilen Daten der Entität als einzige Änderung einen "Besitzerwechsel" - also einen gegenüber dem Original-Objekt veränderten Wert für das Entitätsattribut "Besitzer" (Feld ownerId) aufweisen, gibt der Wertauflöser trotzdem "Kein Wert" ($null) zurück, da der volatile Datenstand trotz der Änderung als unverändert gewertet wird.

images/download/attachments/177910326/image-2024-9-10_10-50-26-version-1-modificationdate-1725958226566-api-v2.png

images/download/attachments/177910326/image-2024-9-10_10-50-48-version-1-modificationdate-1725958247971-api-v2.png

Wird die Option Entitätsattribute ignorieren abgewählt, dann bezieht der Vergleich zwischen Eingabewert und Vergleichsobjekt für das Änderungsprotokoll auch alle Entitätsattribute mit ein.

Für das obige Beispiel bedeutet dies, dass auch ein "Besitzerwechsel" als Änderung gewertet wird, so dass im Rückgabewert des Änderungsprotokoll-Wertauflösers u. a. ein SimpleValueChangeLog-Objekt für das ownerId-Feld zu finden ist.

Option "Änderungen auch für gelöschte und erstellte Werte erzeugen"

Die Option Änderungen auch für gelöschte und erstellte Werte erzeugen ist per Standard abgewählt.

Falls der Vergleich Werte als "erstellt" oder "gelöscht" klassifiziert, erscheint für diese im Rückgabewert des Änderungsprotokoll-Wertauflösers nur der minimale Funktionsumfang. Der jeweilige "Änderungstyp" (changeType), also DELETED oder CREATED wird im Kopf des betreffenden Änderungsprotokoll-Eintrags angegeben, ohne dass alle Details zum Objekt genannt werden.

Beispiel: Als Eingabewert liegen volatile Daten für ein Firmenkonto vor, in denen die serverseitig (noch) gespeicherte Adresse gelöscht wurde. Bei einem Vergleich mit abgewählter Option Änderungen auch für gelöschte und erstellte Werte erzeugen (s. Screenshot rechts oben) wird für das Feld address ein EntityCahngeLog (mit der eingebetteten Adresse als Entität) ausgegeben, das den "Änderungstyp" (changeType) DELETED ausweist und dabei die gelöschte Entität nur über die Felder fromClass und fromId identifiziert.

images/download/attachments/177910326/image-2024-9-10_10-50-26-version-1-modificationdate-1725958226566-api-v2.png

images/download/attachments/177910326/image-2024-9-10_10-51-36-version-1-modificationdate-1725958295627-api-v2.png


Wird die Option Änderungen auch für gelöschte und erstellte Werte erzeugen ausgewählt (s. Screenshot rechts unten), dann liefert im oben beschriebenen Szenario der Vergleich detaillierte Informationen für die gelöschte Adresse, die zu diesem Zweck mit "Kein Wert" ($null). Das EntityChangeLog für das address-Feld enthält zusätzlich zu den auch im Standardfall angeführten Daten (fromClass, fromId) im Listenfeld changeLogs eine ggf. sehr umfangreiche Auflistung von untergeordneten Änderungsprotokollen, so als wäre jedes Merkmal innerhalb der Adresse individuell gelöscht worden. Durch die Rekursion bezieht dies auch Attribute der Adresse, Adresskontakte, deren Attribute usw. mit ein.

Vergleichswert-Konfiguration

Der optionale Parameter Vergleichswert kann verwendet werden, um dem Eingabewert ein Vergleichsobjekt gegenüberzustellen.

Wird auf eine Konfiguration für den Vergleichswert komplett verzichtet (Standard, s. Screenshot rechts), dann wird als Vergleichsobjekt das "Originalobjekt" zum Eingabewert verwendet.

  • Für eine Entität als Eingabewert bedeutet dies, dass ein volatiler und ggf. veränderter Datenstand mit dem serverseitige Stand vom Anfang der Transaktion vergleichen wird.

  • Für alle anderen Eingabewert-Typen wird der Eingabewert selbst als Vergleichsobjekt betrachtet, sodass kein Änderungsprotokoll zu erwarten ist. Der Rückgabewert lautet dann "Kein Wert" ($null).

images/download/attachments/177910326/image-2024-9-10_10-46-16-version-1-modificationdate-1725957976133-api-v2.png


images/download/attachments/177910326/image-2024-9-10_11-15-53-version-1-modificationdate-1725959752831-api-v2.png

In der Konfiguration rechts soll die Adresse der Firma der Session (im Eingabewert) mit der für den Benutzer der Session angegebenen Adresse verglichen als Vergleichsobjekt verglichen werden.

  • Der Firmenadresse im Eingabewert wird hier durch eine Verkettung mit dem Firma der Session-Wertauflöser und einem Objekt-Feld-Wertauflöser bereitgestellt, der auf das address-Feld des Firmenkontos zugreift.

  • Im Parameter Vergleichswert wird ausgehend vom Benutzer der Session-Wertauflöser über den Eingabeobjekt (Typsicher)-Wertauflöser sichergestellt, dass ein Benutzerkonto (und nicht etwa ein Gastbenutzerkonto) vorliegt. Anschließend erfolgt der Zugriff auf das address-Feld, wie beim Eingabewert.

ANMERKUNG◄ Falls ein Gastbenutzer (s. Gastbenutzer) angemeldet ist, liefert der Benutzer der Session-Wertauflöser kein Benutzerkonto (s. Benutzer). Dann gibt der verkettete Eingabeobjekt (Typsicher)-Wertauflöser "Kein Wert" ($null) an den Objekt-Feld-Wertauflöser weiter. Da ein Gastbenutzerkonto nicht über ein address-Feld verfügt, würde dieser aber ohnehin ins Leere greifen. Falls eine Wertauflöserkette für den Vergleichswert konfiguriert ist, die zur Laufzeit "Kein Wert" ($null) als Rückgabewert liefert, wird $null als Vergleichsobjekt verwendet. Falls eine Entität als Eingabewert anliegt, ergibt dies ein EntityChangeLog das die Entität im Eingabewert als "erstellt" (CREATED) bewertet. Ohne Konfiguration für den Vergleichswert würde dagegen $null zurückgegeben, wie oben beschrieben.

Beispiele

Änderungsprotokoll > Einfacher Wert

Die folgenden Beispiele verwenden das JSON-Format zur Beschreibung von Datenstrukturen.

Eingabewert

Vergleichswert

Änderungsprotokoll

"ABC"
"XYZ"
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": "DEF",
"name": null,
"toIndex": -1,
"toValue": "ABC"
}
  • Für einen Textwert (String) wird die Zeichenfolge insgesamt als Wert (fromValue, toValue) betrachtet.

  • Ein Vergleich für die Länge (length), die im Objekt-Feld-Wertauflöse als "Feld" angeboten wird, findet nicht statt, da es sich dabei um eine berechnete Größe handelt.

  • Für einen eigenständigen String spielen der Index (fromIndex, toIndex) und der Name (name) keine Rolle.

4711
3210
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": {
"class": "java.lang.Integer",
"value": 3210
},
"name": null,
"toIndex": -1,
"toValue": {
"class": "java.lang.Integer",
"value": 4711
}
}
  • Ein Zahlenwert wird unter Berücksichtigung des Datentyps (class) als Wert (fromValue, toValue) betrachtet.

  • Im Beispiel oben wird die Ziffernfolge (in JSON) für die verglichenen Werte als Integer interpretiert..

  • Für einen eigenständigen String spielen der Index (fromIndex, toIndex) und der Name (name) keine Rolle.

{ "class": "java.lang.Long", "value": "4711" }


z. B. aus einem Long-Wertauflöser

{ "class": "java.lang.Integer", "value": "4711" }

images/download/attachments/177910326/image-2024-9-10_11-27-29-version-1-modificationdate-1725960448846-api-v2.png

z. B. aus einem Ganzzahl-Wertauflöser

{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": {
"class": "java.lang.Integer",
"value": 4711
},
"name": null,
"toIndex": -1,
"toValue": {
"class": "java.lang.Long",
"value": "4711"
}
}
  • Eingabewert und Vergleichswert unterscheiden sich hier nur im Datentyp (class).

  • Das SimpleValueChangeLog registriert daher insgesamt eine Änderung des Werts, weist dabei aber nicht etwa das Feld class als geändert aus.


Aufzählungswert "Ost" (E) aus der benutzerdefinierten
Dynamischen Aufzählung DIRECTIONS:

images/download/attachments/177910326/image-2024-9-10_11-29-14-version-1-modificationdate-1725960554092-api-v2.png

s. a. Jede dynamische Aufzählung

Aufzählungswert "Nord" (N) aus der benutzerdefinierten
Dynamischen Aufzählung DIRECTIONS:
images/download/attachments/177910326/image-2024-9-10_11-29-51-version-1-modificationdate-1725960591010-api-v2.png

s. a. Jede dynamische Aufzählung

{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": {
"class": "de.lobster.scm.denum.DynamicEnumDto",
"enumName": "DIRECTIONS",
"name": "N"
},
"name": null,
"toIndex": -1,
"toValue": {
"class": "de.lobster.scm.denum.DynamicEnumDto",
"enumName": "DIRECTIONS",
"name": "E"
}
}
  • Die Werte von statischen und dynamischen Aufzählungen werden als "Einfache Werte" verglichen.

  • Für eigenständige Aufzählungswerte spielen der Index (fromIndex, toIndex) und der Name (name) keine Rolle.

Änderungsprotokoll > Collection

Die folgenden Beispiele verwenden das JSON-Format zur Beschreibung von Datenstrukturen.

Eingabewert

Vergleichswert

Änderungsprotokoll

["Tom","Dick","Harry","Sally"]
["Tom","Dick","Harry"]
{
"class": "de.lobster.scm.persistence.history.CollectionChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": null,
"name": null,
"toIndex": 3,
"toValue": "Sally"
}
],
"changeType": null,
"fromIndex": -1,
"fromLength": 3,
"name": null,
"toIndex": -1,
"toLength": 4
}
  • Einer Liste (List) von Textwerten wurde (am Ende) ein Eintrag hinzugefügt.

    ANMERKUNG◄ Tatsächlich wird die Liste im Eingabewert mit einer anderen Liste im Vergleichswert verglichen, die an den drei ersten Positionen dieselben statischen Einträge enthält. Eine echte "Veränderung" an der statisch definierten Liste (z. B. per Liste modifizieren) könnte nicht per Änderungsprotokoll untersucht werden, da kein Original-Objekt als Referenz zur Verüfgung steht.

  • Das CollectionChangeLog registriert einerseits die Änderung der Länge der Liste (fromLength, toLength).

  • Im Listenfeld changeLogs erscheint andererseits ein SimpleValueChangeLog, das für die hinzugefügte Listenposition (toIndex) den neuen Wert (toValue) angibt.

["Tom","Dick","Harry"]
["Tom","Dick","Harry","Sally"]
{
"class": "de.lobster.scm.persistence.history.CollectionChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": 3,
"fromValue": "Sally",
"name": null,
"toIndex": -1,
"toValue": null
}
],
"changeType": null,
"fromIndex": -1,
"fromLength": 4,
"name": null,
"toIndex": -1,
"toLength": 3
}
  • Aus einer Liste (List) von Textwerten wird der letzte Eintrag "entfernt" (s. Anmerkung oben).

  • Das CollectionChangeLog registriert einerseits die Änderung der Länge der Liste (fromLength, toLength).

  • Im Listenfeld changeLogs erscheint andererseits ein SimpleValueChangeLog, der das "Verschwinden" der gelöschten Listenposition (fromIndex, fromValue) als Wertänderung zu "Kein Wert" ($null) protokolliert.

    ►HINWEIS◄ Dass der Listenposition nicht wirklich der Wert $null zugewiesen wurde, erkennt man daran, dass der Index (toIndex) zu -1 wechselt. Mit der Liste ["Tom","Dick","Harry",null] als Eingabewert würde als toIndex hier unverändert 3 gelten.

{ "class": "set", "data": [ "Tom", "Harry" ] }


{ "class": "set", "data": [ "Tom", "Harry", "Dick"] }
{
"class": "de.lobster.scm.persistence.history.CollectionChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": null,
"name": null,
"toIndex": -1,
"toValue": "Dick"
}
],
"changeType": null,
"fromIndex": -1,
"fromLength": 2,
"name": null,
"toIndex": -1,
"toLength": 3
}
  • Einer "Eindeutigen Liste" (Set) wird ein Eintrag hinzugefügt (s. Anmerkung oben).

  • Das CollectionChangeLog registriert einerseits die Änderung der Länge der Liste (fromLength, toLength).

  • Im Listenfeld changeLogs erscheint andererseits ein SimpleValueChangeLog, der den zusätzlichen Eintrag benennt (toValue).

HINWEIS◄ Die "Eindeutige Liste" verwaltet keine Indexnummern für die Einträge. Die Felder fromIndex und toIndex zeigen daher den Standardwert -1.

[1,2,3]
[3,2,1,0]
{
"class": "de.lobster.scm.persistence.history.CollectionChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": 3,
"fromValue": {
"class": "java.lang.Integer",
"value": 0
},
"name": null,
"toIndex": -1,
"toValue": null
},
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": 2,
"fromValue": {
"class": "java.lang.Integer",
"value": 1
},
"name": null,
"toIndex": 0,
"toValue": {
"class": "java.lang.Integer",
"value": 1
}
},
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": 0,
"fromValue": {
"class": "java.lang.Integer",
"value": 3
},
"name": null,
"toIndex": 2,
"toValue": {
"class": "java.lang.Integer",
"value": 3
}
}
],
"changeType": null,
"fromIndex": -1,
"fromLength": 4,
"name": null,
"toIndex": -1,
"toLength": 3
}
  • Zwei Listen mit statischen Integer-Werten werden verglichen. Alle Werte im Eingabewert tauchen auch im Vergleichswert auf, allerdings in teilweise abweichender Reihenfolge. Der Vergleichswert enthält noch ein zusätzliches Element (0) an der letzten Position.

  • Das CollectionChangeLog registriert einerseits die Änderung der Länge der Liste (fromLength, toLength).

  • Das Feld changeLogs weist für folgende Listenpositionen Änderungen per SimpleValueChangeLog aus:

    • Die letzte Position (fromIndex 3) gilt als zu "Kein Wert" ($null) geändert, also gelöscht. Das ist auch an der Änderung des Index

    • Der Wert 1 gilt als "unverändert verschoben" an die erste Position im Eingabewert (toIndex 0).

    • Der Wert 3 gilt als "unverändert verschoben" an die letzte Position im Eingabewert (toIndex 2)

ANMERKUNG◄ Der Wert 2 taucht im Feld changeLogs nicht auf, da er als komplett unverändert gilt.

Besonderer Anwendungsfall: Alle Entitäten in einer Liste vergleichen

Eine mit dem Erzeuge Liste angelegte Collection enthält volatile Daten von unterschiedlichen Entitäten, die gegenüber dem serverseitigen Stand geändert sein können:

images/download/attachments/177910326/image-2024-9-10_14-37-20-version-1-modificationdate-1725971840250-api-v2.png

  • Als erstes Listenelement wird der Wert einer Variablen cneCompany definiert, die auf ein bestimmtes Firmenkonto (s. Firmen) verweist.

  • Als zweites Listenelement wird der Wert einer Variablen cneKeyUser definiert, die auf ein bestimmtes Benutzerkonto (s. Benutzer) verweist.

Beide Konten wurden bereits erstellt und wurden im aktuellen Kontext unter Umständen verändert. Der Änderungsprotokoll-Wertauflöser sollen alle "volatilen" Änderungen an den als Entitäten aufgelisteten Konten feststellen.

images/download/attachments/177910326/image-2024-9-10_14-40-33-version-1-modificationdate-1725972032729-api-v2.png

Als Vergleichswert soll der Liste von Entitäten im Eingabewert eine Liste der zugehörigen "Originalobjekte" gegenübergestellt werden.

Dies kann mit dem Sammle Werte-Wertauflöser erreicht werden, der hier als Wert zum Sammeln auf den Original-Objekt-Wertauflöser zurückgreift, um den serverseitigen Stand für jedes Element der als Eingabewert vorliegenden Liste von Entitäten abzurufen.

{
"class": "de.lobster.scm.persistence.history.CollectionChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.EntityChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": true,
"name": "active",
"toIndex": -1,
"toValue": false
}
],
"changeType": "MODIFIED",
"fromClass": "de.lobster.scm.base.security.user.User",
"fromId": "1901",
"fromIndex": 1,
"name": null,
"toClass": "de.lobster.scm.base.security.user.User",
"toId": "1901",
"toIndex": 1
}
],
"changeType": null,
"fromIndex": -1,
"fromLength": 2,
"name": null,
"toIndex": -1,
"toLength": 2
}
  • Das CollectionChangeLog (rechts) listet im changeLogs-Feld nur einen einzigen Eintrag auf, das das Benutzerkonto an der zweiten Listenposition betrifft.

  • Das changeLogs-Feld im EntityChangelog für diesen Listeneintrag gibt an, dass der Wert für das Feld active von true auf false gewechselt hat. Das Benutzerkonto soll also offenbar deaktiviert werden.

  • Am Firmenkonto wurden offenbar keine Änderungen vorgenommen.

Änderungsprotokoll > Objekt

Die folgenden Beispiele verwenden das JSON-Format zur Beschreibung von Datenstrukturen.

Eingabewert

Vergleichswert

Änderungsprotokoll

{ "class": "de.lobster.scm.utils.date.DateTime",
"dateValue": "...", "timeZone": "Europe/Berlin" }

Ein als Relatives Datum mit Zeit ermitteltes "Datum mit Zeit"-Objekt:

images/download/attachments/177910326/image-2024-9-10_15-49-50-version-1-modificationdate-1725976189821-api-v2.png

  • "Anfang des nächsten Tages" in der Zeitzone Europe/Berlin

{ "class": "de.lobster.scm.utils.date.DateTime",
"dateValue": "...", "timeZone": "Europe/Rome" }

images/download/attachments/177910326/image-2024-9-10_15-51-39-version-1-modificationdate-1725976299127-api-v2.png

  • "Anfang des nächsten Tages" in der Zeitzone Europe/Rome.

{
"class": "de.lobster.scm.persistence.history.ObjectChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": {
"timeZoneId": "Europe/Rome"
},
"name": "timeZone",
"toIndex": -1,
"toValue": {
"timeZoneId": "Europe/Berlin"
}
}
],
"changeType": "MODIFIED",
"fromClass": "de.lobster.scm.utils.date.DateTime",
"fromIndex": -1,
"name": null,
"toClass": "de.lobster.scm.utils.date.DateTime",
"toIndex": -1
}
  • Da der Datentyp "Datum mit Zeit" in Lobster Data Platform / Orchestration als Objekt mit den Feldern dateValue und timeZone definiert ist, ergibt der Vergleich zwischen zwei Datumswerten per Änderungsprotokoll-Wertauflöser ein ObjectChangeLog.

  • Im Beispiel wird der "Anfang des nächsten Tages" für zwei unterschiedliche Zeitzonen verglichen. Da die verglichenen Zeitzonen denselben Versatz zu UTC aufweisen, unterscheiden sich die verglichenen Objekte nur im Feld timeZone, während das dateValue-Feld in beiden Fällen denselben Zahlenwert für die Millisekunden seit 01.01.19700 00:00:00.000 (UTC) ausweist.

{ "name": "Darth Vader", "nickname": "Dee-Vee" }
{ "name": "Anakin" }
{
"class": "de.lobster.scm.persistence.history.ObjectChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": "Anakin",
"name": "name",
"toIndex": -1,
"toValue": "Darth Vader"
},
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": null,
"name": "nickname",
"toIndex": -1,
"toValue": "Dee-Vee"
}
],
"changeType": "MODIFIED",
"fromClass": "flex.messaging.io.amf.ASObject",
"fromIndex": -1,
"name": null,
"toClass": "flex.messaging.io.amf.ASObject",
"toIndex": -1
}
  • Zwei "Client-Objekte" werden miteinander verglichen.

  • Das ObjectChangeLog wertet den Objektzustand im changeType-Feld als "verändert" (MODIFIED).

  • Im changeLogs-Feld werden zwei Änderungen per SimpleChangeLog ausgewiesen:

    • Das name-Feld wird als geändert von "Anakin" (fromValue) zu "Darth Vader" (toValue) gewertet.

    • Das nickname Feld, das nur im Eingabewert vorkommt, wird als von "Kein Wert" ($null) geändert gewertet.

{ "name": "Luke", 
"tools": { "class": "list", "data": [
{ "skill": "TheForce™" },
{ "item": "LightSaber™" } ]
           }
}
{ "name": "Luke" }
{
"class": "de.lobster.scm.persistence.history.ObjectChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.CollectionChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.ObjectChangeLog",
"changeLogs": [
],
"changeType": "CREATED",
"fromClass": null,
"fromIndex": -1,
"name": null,
"toClass": "flex.messaging.io.amf.ASObject",
"toIndex": 0
},
{
"class": "de.lobster.scm.persistence.history.ObjectChangeLog",
"changeLogs": [
],
"changeType": "CREATED",
"fromClass": null,
"fromIndex": -1,
"name": null,
"toClass": "flex.messaging.io.amf.ASObject",
"toIndex": 1
}
],
"changeType": null,
"fromIndex": -1,
"fromLength": 0,
"name": "tools",
"toIndex": -1,
"toLength": 2
}
],
"changeType": "MODIFIED",
"fromClass": "flex.messaging.io.amf.ASObject",
"fromIndex": -1,
"name": null,
"toClass": "flex.messaging.io.amf.ASObject",
"toIndex": -1
}
  • Zwei "Client-Objekte" werden miteinander verglichen.

  • Das ObjectChangeLog wertet den Objektzustand im changeType-Feld als "verändert" (MODIFIED).

  • Das changeLogs-Feld des (äußeren) ObjectChangeLog beinhaltet ein CollectionChangeLog, da im Eingabewert ein zusätzliches Listenfeld tools erkannt wurde.

  • Das changeLogs-Feld des CollectionChangeLog beinhaltet zwei ObjectChangeLog-Instanzen, die außer der Listenposition (toIndex) und dem "Änderungstyp" (CREATED) keine weiteren Details zu den hinzugefügten "Tools" verzeichnen.

    HINWEIS◄ Offensichtlich wurde hier der Änderungsprotokoll-Wertauflöser verwendet, ohne die Option Änderungen auch für gelöschte und erstellte Werte erzeugen auszuwählen. Sonst würde der Rückgabewert auch Informationen über die Objekt-Felder (skill und item) als SimpleValueChangeLog im changeLogs-Feld des betreffenden ObjectChangeLog enthalten.

Änderungsprotokoll > Entität

Die folgenden Beispiele verwenden wahlweise das XML-Format und das JSON-Format zur Beschreibung von Datenstrukturen.

Eingabewert

Vergleichswert

Rückgabewert

Eine Ereignisbehandlung wird durch das Ereignis "Ändern" (s. Allgemein (Ereignisse)) ausgelöst, so dass sie auf das Speichern bereits erstellter Entitäten reagiert. Eine Typprüfung stellt sicher, dass die Ereignisaktion nur im Kontext von Zuordnungskriterien aktiv wird.

Als Eingabewert liegen also immer die ggf. geänderten volatilen Daten des Zuordnungskriteriums vor, das gespeichert werden soll. Vor dem Speichern von Änderungen soll ein Änderungsprotokoll erstellt werden, damit - abhängig von der Art der Änderungen - ggf. ein Administrator benachrichtigt werden kann.

Beispiel:

<?xml version="1.0" encoding="UTF-8"?>
<core:AssociationCriteria ... priority="100" name="XF_MFST_AGV" description="PLEASE CHECK!" onlyForLogic="false">
<core:RuleJunction junctionType="CONJUNCTION">
<core:CheckTypeRule type="mfst:Manifest" isCollectionOf="false" checkNullValue="false"/>
<core:EntityPropertyRule>
<core:EqualsMatcher deepCompare="false">
<core:LiteralValueResolver>
<value enumName="mfst:ManifestType" name="AGV" xsi:type="core:DynamicEnumValue"/>
</core:LiteralValueResolver>
</core:EqualsMatcher>
<core:PropertyResolver property="manifestType"/>
</core:EntityPropertyRule>
</core:RuleJunction>
</core:AssociationCriteria>

  • Im Beispiel wurde dem Zuordnungskriterium nur ein Text für das Feld "Beschreibung" (description) hinzugefügt. Das EntityChangeLog dokumentiert im changeLogs-Listenfeld genau diese eine Änderung per SimpleChangeLog.

nicht konfiguriert, also das Original-Objekt zur betreffenden Entität

{
"class": "de.lobster.scm.persistence.history.EntityChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": "",
"name": "description",
"toIndex": -1,
"toValue": "PLEASE CHECK!"
}
],
"changeType": "MODIFIED",
"fromClass": "de.lobster.scm.association.criteria.AssociationCriteria",
"fromId": "9901",
"fromIndex": -1,
"name": null,
"toClass": "de.lobster.scm.association.criteria.AssociationCriteria",
"toId": "9901",
"toIndex": -1
}

Ausgehend vom geänderten Stand des Zuordnungskriteriums im vorherigen Beispiel hat der benachrichtigte Administrator das Zuordnungskriterium überprüft und möchte es nun mit Änderungen wieder abspeichern.

Die Ereignisbehandlung reagiert wiederum und ermittelt die Änderungen am Zuordnungskriterium über den Änderungsprotokoll-Wertauflöser.

Beispiel:

<?xml version="1.0" encoding="UTF-8"?>
<core:AssociationCriteria ... priority="50" name="XF_MFST_AGV" description="CHECKED AND APPROVED W/CHANGES" onlyForLogic="false">
<core:RuleJunction junctionType="CONJUNCTION">
<core:CheckTypeRule type="mfst:Manifest" isCollectionOf="false" checkNullValue="false"/>
<core:EntityPropertyRule>
<core:EqualsMatcher deepCompare="false">
<core:LiteralValueResolver>
<value enumName="mfst:ManifestType" name="AGV" xsi:type="core:DynamicEnumValue"/>
</core:LiteralValueResolver>
</core:EqualsMatcher>
<core:PropertyResolver property="manifestType"/>
</core:EntityPropertyRule>
<core:AssociationSubCriteriaRule id="2651"/>
</core:RuleJunction>
</core:AssociationCriteria>

  • Das changeLog-Feld im EntityChangeLog listet nun mehrere untergeordnete Änderungsprotokolle für das Zuordnungskriterium auf:

    • Das SimpleValueChangeLog an der ersten Position belegt, dass das Feld "Beschreibung" (description) erneut geändert wurde.

    • Das SimpleValueChangeLog an der zweiten Position belegt, dass die "Priorität" (priority) von 100 auf 50 verringert wurde.

    • Das nachfolgende ObjectChangeLog weist eine Änderung am "Regelwerk" im Feld rule des Zuordnungskriterium aus, der man entlang der Verschachtelung stufenweise "auf den Grund" gehen kann:

      • Das CollectionChangeLog betrifft das Listenfeld rules innerhalb der äußeren RuleJunction, also die Liste der untergeordneten Regeln innerhalb der UND-Verknüpfung (CONJUNCTION):

        • Dass dieser Collection ein zusätzliches Element hinzugefügt wurde belegt schon die Änderung seiner "Länge" (fromLength, toLength).

        • Für das erstellte Element erscheint ein untergeordnetes ObjectChangeLog für dessen changeType-Feld den Wert CREATED angibt. Auch die Position des erstellten Elements wird auf dieser Ebene angegeben (toIndex).

          ANMERKUNG◄ Offenbar wurde die Option Änderungen auch für gelöschte und erstellte Werte erzeugen für den Änderungsprotokoll-Wertauflöser ausgewählt, sodass das changeLogs-Listenfeld weitere Details des hinzugefügten Objekts enthält:

          • Das enthaltene SimpleValueChangeLog gibt den Wert (2651) wieder, der dem id-Feld der hinzugefügten Sub-Zuordnungskriterium-Regel (AssociationSubCriteriaRule) in der Konfiguration zugewiesen wurde.

            Der folgende Screenshot unten zeigt die Regel-Konfiguration im vom Administrator überarbeiteten Stand des Zuordnungskriteriums:

images/download/attachments/177910326/image-2024-9-10_15-48-10-version-1-modificationdate-1725976090200-api-v2.png

nicht konfiguriert, also das Original-Objekt zur betreffenden Entität

{
"class": "de.lobster.scm.persistence.history.EntityChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": "PLEASE CHECK!",
"name": "description",
"toIndex": -1,
"toValue": "CHECKED AND APPROVED W/CHANGES"
},
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": {
"class": "java.lang.Integer",
"value": 100
},
"name": "priority",
"toIndex": -1,
"toValue": {
"class": "java.lang.Integer",
"value": 50
}
},
{
"class": "de.lobster.scm.persistence.history.ObjectChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.CollectionChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.ObjectChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": null,
"name": "id",
"toIndex": -1,
"toValue": {
"class": "java.lang.Long",
"value": "2651"
}
}
],
"changeType": "CREATED",
"fromClass": null,
"fromIndex": -1,
"name": null,
"toClass": "de.lobster.scm.association.criteria.rule.AssociationSubCriteriaRule",
"toIndex": 2
}
],
"changeType": null,
"fromIndex": -1,
"fromLength": 2,
"name": "rules",
"toIndex": -1,
"toLength": 3
}
],
"changeType": "MODIFIED",
"fromClass": "de.lobster.scm.association.criteria.rule.RuleJunction",
"fromIndex": -1,
"name": "rule",
"toClass": "de.lobster.scm.association.criteria.rule.RuleJunction",
"toIndex": -1
}
],
"changeType": "MODIFIED",
"fromClass": "de.lobster.scm.association.criteria.AssociationCriteria",
"fromId": "9901",
"fromIndex": -1,
"name": null,
"toClass": "de.lobster.scm.association.criteria.AssociationCriteria",
"toId": "9901",
"toIndex": -1
}