Einlesen von Dokumentdaten

Für das Einlesen von Daten in die jadice document platform stehen verschiedene Wege zur Verfügung.

Der Ladevorgang kann sowohl ein bereits bestehendes wie auch ein zu diesem Zweck neu erstelltes jadice Document befüllen. Zudem muß entschieden werden, ob ein Ladevorgang synchron oder asynchron ablaufen soll. Synchron geladene Dokumente stehen erst zur Verfügung, nachdem der gesamte Lesevorgang abgeschlossen ist. Das Dokument ist dann vollständig in dem Sinn, dass alle angefragten Daten verfügbar sind. Beim asynchronen Laden werden die Dokumentdaten im Hintergrund eingelesen. Es wird daher zunächst ein unvollständiges Dokument zurückgegeben mit dem zwar bereits gearbeitet werden kann, das aber durch den Lade-Thread noch verändert wird.

Ein erstelltes Dokument kann bereits in »unbefülltem« Zustand der PageView zur Anzeige übergeben werden. Diese erkennt automatisch, wenn die erste Seite (beziehungsweise das erste Seitensegment in einer Seite) geladen wurde und zeigt die Inhalte bereits an, obwohl im Hintergrund der Ladevorgang noch nicht abgeschlossen sein muss. Dies ist insbesondere bei langsamen Netzwerkverbindungen und großen Dokumenten von Vorteil.[21]

Für den eigentlichen Lesevorgang stehen zwei unterschiedliche APIs zur Verfügung: Die Reader API, die einen imperativen Ansatz verfolgt, sowie die Fluent Read API, die konfigurationsbasiert arbeitet. Beide werden in den folgenden Abschnitten vorgestellt.

Reader API

Die Reader API, rund um deren zentrale Klasse Reader, bietet die klassische Programmierschnittstelle zum Einlesen von Dokumentdaten in jadice Dokumente. Instanzen von Reader können über den parameterlosen Konstruktor erzeugt werden. Sie bieten die Möglichkeit, Lesevorgänge zu konfigurieren und zu starten.

Grundsätzlich gilt, dass jeweils ein logisch zusammengehöriger Lesevorgang auf einer dedizierten Instanz von Reader durchgeführt werden sollte. Als logisch zusammengehörig wird in diesem Fall ein Lesevorgang bezeichnet, der alle Datenströme umfasst, die notwendig sind um das gewünschte Dokument vollständig einzulesen. Dies kann beispielsweise die Datenströme für Seiteninhalte und Annotationen umfassen.[22]

Der Programmieransatz zur Verwendung der Reader API folgt dem imperativen Programmierparadigma. Einer Instanz von Reader werden aufeinanderfolgend Anweisungen erteilt, die entweder interne Statusänderungen oder einzelne (Teil-)Lesevorgänge zur Folge haben. Auf diese Weise wird ein jadice-Dokument nach und nach mit Inhalten befüllt.

Der Reader bietet verschiedene read(…)-Methoden, die auf unterschiedliche Weisen Datenströme entgegennehmen und einlesen. Die Lesevorgänge erfolgen jeweils mit den zum Zeitpunkt des Methodenaufrufs vorliegenden Einstellungen des Readers. Typischerweise sieht der Gesamtablauf daher so aus, dass abwechselnd Einstellungen auf dem Reader getroffen werden und darauf basierend Streams eingelesen werden.

Unter anderem stehen folgende Einstellungen auf dem Reader zur Verfügung:

  • Dokument in das gelesen werden soll. Ist kein Dokument gesetzt, so wird vor dem Lesevorgang ein leeres Dokument erzeugt.

  • Zielposition (Seitenzahl) der Seite im Dokument. Diese Einstellung wird target index genannt und durch den Reader automatisch inkrementiert falls ein mehrseitiges Dokument aus einem Datenstrom gelesen wird. Zur Vereinfachung existieren zwei Konstanten, die festlegen, dass neue Seiten stets vor der ersten oder nach der letzten vorhandenen Seite eingefügt werden sollen.

  • LayerMapping das bestimmt, in welche Seitenebene die erzeugten Seitensegmente abgelegt werden sollen.

  • Dokumentformat, das für den Lesevorgang verwendet werden soll.

Sämtliche Methoden der Klasse Reader arbeiten synchron, sodass mit der Rückkehr aus dem Methodenaufruf der gesamte Arbeitsschritt abgeschlossen ist. Ist Nebenläufigkeit gewünscht, wird dies typischerweise realisiert indem die gesamte Arbeit mit einem Reader-Objekt – also ein logisch zusammengehöriger Lesevorgang – in eine Instanz von Runnable verpackt und auf einem eigenen Thread ausgeführt wird. Der Reader selbst ist für die Verwendung auf einem einzigen Thread konzipiert und darf nicht konkurrierend verwendet werden.

Der Reader bietet die Möglichkeit, Lesevorgänge abzubrechen. Zu diesem Zweck stellt er eine Instanz von Canceler zur Verfügung, welche die Möglichkeit bietet, einen Abbruch anzufordern. Dieses Objekt ist thread-safe – seine Methoden können daher, im Gegensatz zu denen des Reader, von jedem Thread aus aufgerufen werden. Dazu ist keine Synchronisierung nötig. Wann der angeforderte Abbruch tatsächlich ausgeführt wird, ist abhängig vom Format, das gelesen wird. Jedes Format stellt somit sicher, dass ein günstiger Zeitpunkt für den Abbruch gewählt wird. Ein abgebrochener Reader kann nicht mehr sinnvoll weiterverwendet werden.Aufrufe der read(…)-Methoden eines abgebrochenen Readers führen zu keinen weiteren Lesevorgängen; sie führen jedoch auch nicht zu Exceptions.

Häufig (und besonders wenn der Lesevorgang auf einen anderen Thread stattfindet) besteht in einzelnen Programmteilen Interesse daran, Informationen über den Fortschritt (oder Abbruch) eines Dokument-Lesevorgangs zu erhalten. Zu diesem Zweck bietet der Reader die Möglichkeit, Instanzen von ReaderListener zu registrieren, die über Änderungen informiert werden.[23]

Naturgemäß arbeiten Instanzen von Reader eng mit den Dokumenten zusammen, in denen sie gelesene Daten ablegen. Aus diesem Grund wird durch Lesevorgänge automatisch der Dokumentstatus der aktuell gesetzten Instanz von Document geändert. Konkret wird bei jedem Aufruf einer read(…)-Methode der Dokumentstatus auf Document.BasicState.LOADING gesetzt – falls dieser Status nicht sowieso schon gesetzt ist. Ist das Lesen eines einzelnen Datenstroms abgeschlossen, bleibt der Status weiterhin gesetzt. Da der Reader keine Information darüber besitzt, wann sämtliche logisch zusammengehörende Streams eingelesen wurden, ist es Aufgabe des Integrators zum geeigneten Zeitpunkt das Dokument in den Status Document.BasicState.READY zu versetzen.[24]

Um die generell empfohlene Vorgehensweise der Verwendung einer dedizierten Instanz von Reader pro logisch zusammengehörendem Lesevorgang möglichst einfach zu gestalten, bietet die Klasse eine Komfort-Methode an, um den Abschluss des Lesevorgangs zu markieren. Es handelt sich um die Methode complete(). Diese benachrichtigt alle registrierten ReaderListener darüber, dass der logisch zusammengehörende Lesevorgang abgeschlossen ist. Außerdem setzt sie, sofern das Dokument nach wie vor den Status Document.BasicState.LOADING hat, einen geeigneten neuen Dokumentstatus: Document.BasicState.READY bei vollständigem Abschluss der Lesevorgänge, oder Document.BasicState.UNKNOWN falls der Abbruch angefordert wurde. Ein Reader, auf dem die Methode complete() aufgerufen wurde, kann danach nicht mehr für weitere Lesevorgänge genutzt werden.

Fluent Read API

Die Fluent Read API bietet Methoden, deren Aufrufe im Fluent Stil aneinander gereiht werden können. Auf diese Weise lässt sich leicht lesbarer Code erzeugen. Die Fluent Read API ist für einfache Ad-Hoc Lesevorgänge gut geeignet. Sie bietet weniger Flexibilität als der Reader. Es können keine ReaderListener registriert werden und der Abbruch von Lesevorgängen wird nicht unterstützt.

Das Lesen von Dokumenten geschieht prinzipiell in drei Schritten:

  1. Erzeugen eines ReadConfigurer

  2. Konfiguration der zu lesenden Dokumentstrukturen (Documents)

  3. Einlesen der Dokumentdaten

Auf diese Weise erzeugte Dokumente stehen zur weiteren Verarbeitung und insbesondere für die Anzeige direkt zur Verfügung. Die folgenden Abschnitte gehen auf die einzelnen Schritte näher ein.

Schritt 1: Erzeugen eines ReadConfigurer

Um Dokumentdaten einzulesen muss zunächst eine Instanz von ReadConfigurer erzeugt werden. Es existieren dafür zwei unterschiedliche Varianten, die den Lesevorgang entweder synchron oder asynchron durchführen. In der synchronen Variante werden alle Lesevorgänge auf dem aktuellen Thread durchgeführt. Die asynchrone Variante lagert diese auf einen TaskExecutor aus. Erzeugt werden die Konfigurationen durch statische Methodenaufrufe der Klasse Read.

Schritt 2: Konfiguration der zu lesenden Dokumentstrukturen (Documents)

Der ReadConfigurer arbeitet mit Instanzen der Klasse ReadConfiguration zusammen, die spezifizieren, wie Dokumente aus zugrundeliegenden Datenquellen zusammengesetzt werden sollen. Falls zur Parametrisierung direkt die Methoden des ReadConfigurer benutzt werden, geschieht diese Zusammenarbeit implizit. Oft ist es jedoch sinnvoll, eine eigene Implementation der ReadConfiguration bereitzustellen. Dies ist insbesondere interessant, wenn es sich um komplexe Zusammensetzungen handelt oder die Konfiguration wiederverwendbar sein soll.

Unabhängig davon, auf welchem Weg die Konfiguration vorgenommen wird, sehen die Methodenaufrufe ähnlich aus. Unter „Beispiele zur Fluent Read API finden sich Beispiele für das Einlesen von Daten auf einfachem Weg, sowie mittels einer ReadConfiguration.

Schritt 3: Einlesen der Dokumentdaten

Nachdem die Konfiguration vorgenommen ist, kann der Lesevorgang ausgelöst werden. Dies geschieht durch Aufruf der execute() Methode auf dem ReadConfigurer. Ab diesem Zeitpunkt ist es möglich, das erzeugte Dokument vom ReadConfigurer zu erfragen.

Datenformate

Die jadice document platform erlaubt das Einlesen von Dokumenten aus unterschiedlichen Datenformaten.[25] Jedes unterstützte Format wird durch eine Instanz des Typs Format repräsentiert. Sie stellt allgemeine Informationen wie beispielsweise den gültigen MIME-Type oder typische Dateiendungen zur Verfügung. Ein wichtiger Verwendungszweck der Format Instanzen ist die Bereitstellung von Informationen. Beispielsweise erhalten Dokumentdaten die Informationen darüber, aus welchem Datenformat sie gelesen wurden. Zu diesem Zweck bietet jede PageSegmentSource Zugriff auf die repräsentative Format Instanz. Der zweite Bereich in dem Format Instanzen relevant sind, ist der Lesevorgang.

Wie in den vorhergehenden Abschnitten beschrieben, werden Lesevorgäng im Regelfall durch einen Reader angestoßen. Intern erfolgt daraufhin eine Delegation an formatspezifische Ausprägungen der Klasse FormatReader. Diese verwandeln die Input-Daten (Datenstrom, siehe „Datenströme und Verwaltung von Ressourcen“) in eine jadice Dokumentenrepräsentation. Im Normalfall wird zu Beginn eines Lesevorgangs der zuständige FormatReader automatisch ermittelt. Dazu wird zunächst überprüft, welche Instanzen im Klassenpfad als Services zur Verfügung stehen. Im Anschluss daran wird jede gefundene Instanz befragt, ob sie den gegebenen Datenstrom verarbeiten kann. Ist eine solche Instanz gefunden, ist das Format des Datenstroms ermittelt und der eigentliche Lesevorgang beginnt.

Alternativ zu diesem Automatismus bietet der Reader die Möglichkeit, eine Instanz von Format explizit zu setzen.[26] In diesem Fall entfällt die Formatbestimmung und es kann sofort mit dem Laden der Daten begonnen werden. Somit können Ladevorgänge beschleunigt und Formatverwechselungen vermieden werden. Es ist jedoch zu beachten, dass bei Angabe eines nicht zum Datenstrom passenden Formats der Ladevorgang abgebrochen wird und eine Suche nach alternativen Formaten nicht stattfindet.

Für einzelne Formate ist die automatische Erkennung nicht möglich, da der Datenstrom keine eindeutigen Informationen liefert. In diesen Fällen muss das gewünschte Format zwingend dem Reader mitgeteilt werden. Ein typisches Beispiel sind die Annotationsformate: Die jadice document platform unterstützt IBM ContentManager-kompatible Annotationen. Diese Annotationen werden vom Archiv als MO:DCA Strukturen gehalten. Lädt man Annotationen ohne Angabe des gewünschten Formats, kann es zur Verwechslung zwischen einem eigenständigen MO:DCA-Dokument und Annotationen als zusätzlichen Dokumentinformationen kommen.

Formatspezifische Einstellungen

Für einzelne Formate kann es unter Umständen sinnvoll sein, den Lesevorgang näher zu parametrisieren. Diese Parameter können beispielsweise die Entscheidungen beeinflussen, die bei der Interpretation von Daten getroffen werden müssen, oder Grundannahmen und Voreinstellungen definieren. Welche Konfigurationsmöglichkeiten zur Verfügung stehen und welche Auswirkungen sich durch eine Änderung ergeben, ist formatspezifisch.

Falls ein Format Einstellmöglichkeiten bietet, stellt es diese als konkrete Ausprägungen von ReaderSettings zur Verfügung. Dem unter „Processing Controls und Settings“ beschriebenen Konzept folgend werden die unterschiedlichen Settings in einem ReaderControls Objekt gesammelt. Daraus werden sie über ihren Klassennamen bereitgestellt.

Um den Umgang mit Einstellungen für einen Lesevorgang zu erleichtern, verwaltet der Reader intern eine Instanz von ReaderControls und bietet direkt Zugriffsmethoden für ReaderSettings an. Die Settings die auf einer Reader Instanz gesetzt werden, gelten für alle nachfolgend mit dieser Instanz gestarteten Lesevorgänge.

Auf Änderungen am Lesevorgang hören

Die Schnittstelle ReaderListener steht Integratoren zur Verfügung, wenn ein Interesse am Ablauf der Leseprozesse von Dokumenten besteht. Der Reader informiert registrierte ReaderListener über den aktuellen Fortschritt.[27] Der Zustand des Lesevorgangs wird über eine Instanz von ReaderListener.ReaderEvent beschrieben, die unter Anderem folgenden Angaben enthalten kann:

  • Art des Ereignisses (Lesevorgang gestartet, abgeschlossen oder abgebrochen), repräsentiert durch die Konstanten aus ReaderListener.ReaderEvent.Type.

  • Format das für den Lesevorgang verwendet wurde

  • Dokument, Seite und Seitensegment in die gelesen wurde

  • Im Fehlerfall die auslösende Exception

Welche der möglichen Angaben im Event tatsächlich befüllt sind, hängt von der Art des Ereignisses ab. Da sich beispielsweise die Meldung über den erfolgreichen Abschluss eines Lesevorgangs auf den Gesamtvorgang bezieht, kann sie keine spezifische Seite referenzieren.

Die Art des Ereignisses unterteilt sich in die folgenden drei Kategorien:

  1. Formatspezifische Ereignisse, die das Lesen aus einem einzelnen Datenstrom betreffen.

    Es wird über Beginn und Abschluss eines formatspezifischen Lesevorgangs informiert. Neben einem regulären Abschluss kann es auch zum Fehlerfall oder einem vorzeitigen Abbruch kommen.

  2. Allgemeine Ereignisse, die eine einzelne Seite betreffen.

    Es wird über Beginn und Abschluss des Lesevorgangs für eine Einzelseite informiert.

  3. Allgemeine Ereignisse, die den Gesamt-Lesevorgang betreffen.

    Es wird über den Abschluss eines Lesevorgangs informiert. Dieses Ereignis wird indirekt dadurch ausgelöst, dass im Rahmen einer Integration die Methode Readercomplete() aufgerufen wird.

Zusätzlich zu den allgemeinen Angaben der beschriebenen Events erhalten Instanzen von ReaderListener im Fehlerfall auch detaillierte Informationen zu formatspezifischen Warnungen oder Problemen im Lesevorgang. Um dies zu erreichen erweitert ReaderListener das Inteface QualifiedLogListener. Weiterführende Informationen finden sich unter„Qualified LogEvents und Qualified Logging“.

Typische Implementationen von ReaderListener interessieren sich lediglich für eine Teilmenge der ankommenden Ereignisse. Oft soll nur auf einzelne Statusübergänge, wie beispielsweise der Abschluss eines logisch zusammengehörigen Lesevorgangs, reagiert werden. Um Implementationen einfacher zu gestalten existiert daher die Klasse ReaderListenerAdapter. Sie nimmt ankommende Ereignisse entgegen und leitet diese, abhängig von ihrem Typ, an dedizierte Methoden weiter. Diese haben eine leere Default-Implementation und können daher selektiv überschrieben werden.



[21] Soll mit einem Dokument bereits während des Ladevorgangs interagiert werden, sollten notwendige Permissions frühestmöglich gesetzt werden. Dies kann bereits vor dem Lesevorgang erfolgen da Permissions nur für User-Interaktionen gelten.

[22] Die Vorgabe, zusammengehörige Datenströme mit derselben Instanz von Reader zu lesen, ist besonders für den Umgang mit Annotationen wichtig. Diese können unter Umständen nicht auf der korrekten Seite positioniert werden falls Annotations-Datenströme mit einer unabhängigen Reader-Instanz gelesen werden. Nähere Details sind abhängig vom Annotations-Format und werden in der Annotations-Dokumentation (Annotations-Dokumentation) gegeben.

[24] Dieser Status sollte unbedingt gesetzt werden, da manche jadice-Komponenten ihre Arbeit nur aufnehmen können, wenn das zugeordnete Dokument als READY markiert ist.

[25] Eine Übersicht der unterstützten Formate gibt „Formate“.

[26] Beispielhafter Quellcode findet sich unter Beispiel 7.2, „Dokument und Annotationen gemeinsam lesen“.

[27] Die Fluent Read API bietet aufgrund ihrer Ausrichtung auf einfache Ad-Hoc Lesevorgänge kein Listener-Konzept.

[jadice document platform Version 5.5.12.1: Dokumentation für Entwickler. Veröffentlicht: 2021-08-17]