Standard-Engine (Engine std)

In der Standard-Engine werden Funktionen zum dateiorientierten Dokumentenmanagement implementiert. Das sind insbesondere Funktionen zum Speichern und Laden von Dokumenten, zur Archivierung und zur Realisierung des Dokumentenaustausches zwischen mehreren Servern.

Der Standard-Engine unterliegt die Verwaltung des Work-, Cache- und Archivbereichs des Applikationsservers.

3. Dokument-Digest

3.1. Bildung des Digest-Werts

Der Digest-Wert eines Dokuments ist kein einfacher Dateihash, sondern ein zweistufiger SHA-256-Wert über alle Dateien des Dokuments:

  1. Für jede Datei des Dokuments wird SHA-256 über den unveränderten Dateiinhalt gebildet.

  2. Jeder dieser Hashwerte wird als Hex-String in Grossbuchstaben (64 Zeichen, 0–9, A–F) dargestellt und in der Reihenfolge der Dateien ohne Trennzeichen aneinandergehängt.

  3. Über diese Zeichenkette — ASCII-kodiert — wird erneut SHA-256 gebildet. Das Ergebnis, wiederum als Hex-String in Grossbuchstaben, ist der Digest-Wert des Dokuments.

Digest = SHA256( HEX_UPPER(SHA256(Datei_1)) + HEX_UPPER(SHA256(Datei_2)) + … )

Referenzimplementierung:

import hashlib

def document_digest(paths):
    """Two-stage SHA-256 over all files of a document, as stored in osdochash.oshash."""
    parts = "".join(
        hashlib.sha256(open(path, "rb").read()).hexdigest().upper() for path in paths
    )
    return hashlib.sha256(parts.encode("ascii")).hexdigest().upper()

Zu beachten:

  • Die zweite SHA-256-Runde entfällt auch bei einem Dokument mit genau einer Datei nicht. Der Digest-Wert eines einseitigen Dokuments ist also nicht der SHA-256-Hash dieser Datei.

  • Die Reihenfolge der Dateien geht in den Digest-Wert ein: Dieselben Dateien in anderer Reihenfolge ergeben einen anderen Digest-Wert.

  • Der so gebildete Wert ist direkt mit dem Ausgabeparameter TableDigest von std.GetDocumentDigest bzw. mit der Spalte oshash der Tabelle osdochash vergleichbar; eine Normalisierung ist nicht nötig.

  • Die Suche über einen Digest-Wert arbeitet unabhängig von der Schreibweise: Sowohl std.FindDocumentDigest als auch dms.GetObjectsByDigest finden einen Treffer auch bei kleingeschriebenem Hex-String.

Das Verfahren ist gegen einen enaio®-Server der Version 12.0 empirisch verifiziert (Dokumente mit einer, zwei und drei Dateien, Text- und Bilddateien) und nicht Bestandteil der offiziellen Schnittstellenbeschreibung.

3.2. Digest-Werte in Jobs

Job Rolle des Digest-Werts

std.GetDocumentDigest

Liefert den in der Datenbank gespeicherten Wert (TableDigest) und auf Wunsch den neu berechneten Wert (LocalDigest) — der Vergleich beider Werte ist die Integritätsprüfung.

std.CalcDocumentDigest

Berechnet den Wert serverseitig neu und hinterlegt ihn (optional mit Signatur).

std.FindDocumentDigest

Sucht Dokumente zu einem gegebenen Digest-Wert (ohne Versionen).

dms.GetObjectsByDigest

Wie std.FindDocumentDigest, zusätzlich mit Validitätsprüfung der Treffer.

std.StoreInCache

Der Client übergibt seinen lokal berechneten Wert, um eine überflüssige Dateiübertragung zu vermeiden.

std.FileTransfer

Berechnet mit Flags = 5 den Digest-Wert über die angegebenen Dateien.

4. Interne Jobs

Die internen Jobs werden im Allgemeinen nur von der Standard-DMS-Engine selbst verwendet.

6. Capture- und Processing-Messages (CP)

6.1. Mechanismus

Der Content- und Processing-Mechanismus (CP) realisiert die asynchrone Übergabe von Aufträgen zwischen dem enaio®-Server und externen Service-Instanzen — typischerweise dem Volltext-Indexer, dem Rendition-Cache und dem Page-Count-Service. Die Aufträge werden serverseitig in der Tabelle oscpmqueue gehalten und von den Service-Instanzen per Pull-Verfahren abgeholt und nach Verarbeitung wieder quittiert.

Jeder Auftrag (CP-Message) ist über eine MessageGUID identifiziert und einer logischen Queue (Spalte queuename) zugeordnet. Eine Service-Instanz identifiziert sich gegenüber dem Server über einen ServiceName (Instanz-ID) und holt Aufträge gezielt aus einer oder mehreren benannten Queues ab.

6.2. Lebenszyklus einer Nachricht

  1. Erzeugen — Aufträge werden serverseitig in oscpmqueue eingefügt — entweder explizit über std.CreateCPMessages oder durch interne Trigger (z. B. nach Indexdatenänderung). lock_service bleibt zunächst NULL; die Nachricht ist damit unreserviert.

  2. Abholen — Eine Service-Instanz ruft std.GetNextCPMessage zyklisch auf. Der Server reserviert ggf. eine Nachricht für diese Instanz und liefert MessageGUID, ObjectID, ObjectType und QueueName zurück. Eine leere Antwort (MessageGUID leer, Rückgabewert dennoch 0) ist regulär — sie signalisiert lediglich, dass in den abgefragten Queues aktuell keine freie Nachricht vorliegt.

  3. Verarbeiten — Die Service-Instanz arbeitet den Auftrag ab. Typische Folge-Jobs sind std.GetCPObjectInfo für die Objektmetadaten und std.GetCPObjectIdxFulltext für den extrahierten Volltext eines Objekts.

  4. Quittieren — Nach Abschluss meldet die Service-Instanz das Ergebnis über std.DispatchCPMessage mit Reason zurück. Der Server entfernt die Nachricht aus der Queue oder hält sie zur erneuten Verarbeitung bereit, je nach Begründung. Ohne Dispatch-Aufruf bleibt die Nachricht mit gesetztem lock_service reserviert liegen.

  5. Reset — Bricht eine Service-Instanz ab oder startet sie neu, kann sie mit std.ResetServiceCPMessages sämtliche von ihr gehaltenen Reservierungen freigeben — die Aufträge stehen damit wieder anderen Instanzen zur Abholung zur Verfügung.

Zusätzlich meldet der Rendition-Service über std.CPRenditionChanged eine geänderte Rendition zurück an den Server, was wiederum neue Volltext-Messages erzeugen kann.

6.3. Auswahl- und Sperrverhalten

Aus den unreservierten Nachrichten der angefragten Queue wählt der Server diejenige mit dem jüngsten Eintrag (MAX(created)) — also die neueste Nachricht zuerst.

Die Reservierung erfolgt über optimistisches Locking: lock_service und eine zufällig erzeugte checkguid werden nur gesetzt, wenn die Nachricht im Moment des Updates noch unreserviert ist. Anschliessend prüft der Server die zurückgelesene checkguid gegen den eigenen Wert. Stimmen sie nicht überein — etwa weil eine andere Service-Instanz zeitgleich reserviert hat — gilt der Pickup als verloren, und die Service-Instanz erhält eine leere Antwort.

6.4. Zusammenführen redundanter Nachrichten

Beim erfolgreichen Abholen einer Nachricht löscht der Server alle weiteren noch unreservierten Nachrichten für dasselbe (osid, queuename)-Paar. Mehrfach in kurzer Zeit für dasselbe Objekt ausgelöste Aufträge werden so automatisch zu einer einzigen Verarbeitung zusammengeführt; die Service-Instanz arbeitet stets auf dem aktuellsten Stand.

6.5. Bekannte Queues

Queue Typischer Konsument Aufgabe

FULLTEXTIDX

Volltext-Indexer

Objekt für die Volltext-Indexierung anmelden.

FULLTEXTDOC

Volltext-Indexer

Volltext-Dokument indexieren (Inhalt aus dem Volltext-Cache lesen und in den Index aufnehmen).

FULLTEXTFILTER

Volltext-Indexer

Filter-/Vorverarbeitungsschritt vor der Indexierung.

FULLTEXTLOCATION

Volltext-Indexer

Standort-/Container-Indexierung (typisch für Ordnerobjekte).

FULLTEXTDELETE

Volltext-Indexer

Index-Eintrag für ein gelöschtes Objekt entfernen.

RENDITION

Rendition-Cache

Rendition (z. B. PDF-/Text-Extraktion) für ein Dokument erzeugen.

RENRESET

Rendition-Cache

Rendition-Cache zurücksetzen und neu aufbauen.

PAGECOUNT

Page-Count-Service

Seitenanzahl eines Dokuments ermitteln und im Index hinterlegen.

Eine Service-Instanz kann mehrere Queues gleichzeitig bedienen; die Zuordnung „Queue → Konsument" oben spiegelt die typische Konfiguration wider und wird vom Server nicht erzwungen.

7. Nicht dokumentierte Endpunkte

Folgende Jobs werden vom enaio®-Server in der std-Engine bereitgestellt, sind in dieser Dokumentation aber derzeit nicht beschrieben:

Capture- und Processing-Messages (CP)

std.GetCPMessageStatistics, std.GetCPObjectFulltext, std.ProcessPageCountCPMessages, std.ResetCPMessage, std.ResetSelectiveCPMessages

Work-Verzeichnis

std.CopyInWork, std.DeleteInWork

Archivierung und Status

std.SetArchivableFlag, std.StoreInArchive

Volltext

std.DeleteFulltext

Sonstige Datei- und Dokumentverwaltung

std.CalcDocumentMimeType, std.ConfigMedienExt, std.CopyRemark, std.DirectTransformObject, std.GetDocSize, std.GetDocumentRetentions, std.GetDocumentsForAxachash, std.GetMediumSizes, std.GetMigrationObjectInfo, std.GetNextInteger, std.GetVarcInformation