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.
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 Reader
s. 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.
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:
-
Erzeugen eines
ReadConfigurer
-
Konfiguration der zu lesenden Dokumentstrukturen (
Document
s) -
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
(Document
s)
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
.
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.
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.
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.
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:
-
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.
-
Allgemeine Ereignisse, die eine einzelne Seite betreffen.
Es wird über Beginn und Abschluss des Lesevorgangs für eine Einzelseite informiert.
-
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
Reader
complete()
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 (Annotationen) gegeben.
[23] Details dazu unter „Auf Änderungen am Lesevorgang hören“
[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.
[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.