Schritt 2: Konfiguration der zu lesenden Dokumentstrukturen (Document
s)
Nachdem die Erzeugung des ReadConfigurer
abgeschlossen ist, kann dessen weitere
Parametrisierung erfolgen. 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.
Zum Einstieg zunächst ein einfaches Beispiel ohne spezielle ReadConfiguration
. Es werden darin ein
InputStream
und eine asynchroner ReadConfigurer
bereitgestellt. Letzterer wird dann
angewiesen, aus den Inhalten des InputStream
s ein Dokument zu
erstellen.
final InputStream inputStream = [...] AsyncReadConfigurer conf = Read.asynchronously(); conf.newDocument().append(inputStream); conf.execute();
Der abschließende execute
-Aufruf beendet die
Konfigurationschritte und startet den Ladevorgang. Sollen mehrere Dokumente hintereinander
geladen werden, kann dies durch weitere append
Aufrufe umgesetzt
werden.
final InputStream inputStream1 = [...] final InputStream inputStream2 = [...] AsyncReadConfigurer conf = Read.asynchronously(); conf.newDocument().append(inputStream1).append(inputStream2); conf.execute();
Komplexere Lesevorgänge mit ReadConfiguration
und TaskBuilder
Für zusammengesetzte Ladevorgänge ist die Verwendung einer ReadConfiguration
sinnvoll. Das vorhergehende Beispiel
würde dann folgendermaßen umgesetzt werden, wobei hier der Einfachheit halber eine anonyme
Klasse verwendet wird.
final InputStream inputStream1 = [...]
final InputStream inputStream2 = [...]
AsyncReadConfigurer conf = Read.asynchronously();
conf.add(new ReadConfiguration() {
@Override
protected void doConfigure() {
newDocument().append(inputStream1).append(inputStream2);
}
});
conf.execute();
Obwohl diese Variante zunächst in einer größeren Anzahl Codezeilen resultiert, bietet sie doch einen entscheidenden Vorteil: Sie erlaubt es, regelmäßig wiederkehrende Fälle in einer dedizierten Klasse abzulegen. Typische Anwendungsszenarien sind zusammengesetzes Laden von verschiedenen Datenquellen in ein Dokument oder Ladevorgänge die spezielle Schritte, wie die Anmeldung an ein Backend-System, erfordern.
Im Rahmen unseres Beispiels nehmen wir an, dass für Lesevorgänge auf dem Repository
eine ID notwendig ist, auf deren Basis der InputStream
erzeugt
werden kann.
final AsyncReadConfigurer conf = Read.asynchronously(); conf.add(new RepositoryReadConfig()); conf.execute(); [...] // ReadConfiguration als statische innere Klasse public static class RepositoryReadConfig extends ReadConfiguration { @Override protected void doConfigure() { newDocument().append(getInputStreamForID(performRepositoryMagicToGetId())); } private InputStream getInputStreamForID(String id) { // do magic repository reading and return Stream } private String performRepositoryMagicToGetId(){ // do really, really magic stuff ... } }
Zu ihrer vollen Geltung kommt die Eleganz der Fluent Reader
API dort, wo es notwendig ist, komplexere
Dokumentstrukturen aus verschiedenen Datenströmen zu aggregieren. Ein typischer
Anwendungsfall hierfür ist das Laden eines Dokuments mit zugehörigen Annotationen aus zwei
separaten Datenströmen. Da jede Page
in der jadice document platform konzeptuell aus
mehreren Ebenen (DocumentLayer
s) aufgebaut ist, bedarf es einer
Möglichkeit, Inhalte gezielt in eine Ebene zu laden.
Für diesen erweiterten Anwendungsfall ist aus technischen Gründen die Hilfe eines
TaskBuilder
s notwendig, der es erlaubt
Konfigurationsschritte weiter zu konkretisieren. Die im Beispiel erweiterte Klasse ReadConfiguration
stellt mit
protected TaskBuilder task(InputStream source) {...}
einen solchen zur Verfügung. Sollen mehrere Streams auf eine Seite geladen werden,
müssen diese zudem miteinander gruppiert werden. Dies geschieht durch einen Aufruf von
appendGroup()
. Streams, die der Gruppe angehängt werden sollen,
können durch add(...)
hinzugefügt werden. Mit einem Aufruf von
append(...)
oder appendGroup()
wird hingegen
eine neue Seite begonnen.
Die Beispielklasse RepositoryReadConfig
sieht mit den
Änderungen folgendermaßen aus:
// ReadConfiguration als statische innere Klasse public static class RepositoryReadConfig extends ReadConfiguration { @Override protected void doConfigure() { String id = performRepositoryMagicToGetId(); newDocument() .appendGroup() .add(task(getContentInputStream(id))) .add(task(getAnnoInputStream(id)).mapDefaultLayer().toAnnotationsLayer()); } private InputStream getContentInputStream(String id) { // ... } private InputStream getAnnoInputStream(String id) { // ... } private String performRepositoryMagicToGetId(){ // do really, really magic stuff ... } }
Die Konfiguration kann auch mehr als zwei Ebenen enthalten. Dies zeigt das folgende Beispiel mit Quelltext und Screenshot:
// ReadConfiguration als statische innere Klasse public static class RepositoryReadConfig extends ReadConfiguration { @Override protected void doConfigure() { String id = performRepositoryMagicToGetId(); newDocument() .appendGroup() .add(task(getBackgroundInputStream(id)).mapDefaultLayer().toBackgroundLayer()) .add(task(getContentInputStream(id))) .add(task(getWatermarkInputStream(id)).mapDefaultLayer().toFormLayer()) .add(task(getAnnoInputStream(id)).mapDefaultLayer().toAnnotationsLayer()); } private InputStream getAnnoInputStream(String id) { // ... } private InputStream getWatermarkInputStream(String id) { // ... } private InputStream getContentInputStream(String id) { // ... } private InputStream getBackgroundInputStream(String id) { // ... } private String performRepositoryMagicToGetId(){ // do really, really magic stuff ... } }