Skip to main content

Dokumente

jadice web toolkit Dokumentenmodell

Das Dokumentenmodell wurde von der jadice document platform 5 übernommen und leicht erweitert. Hinzugekommen sind sogenannte PageSegmentHandles innerhalb der PageSegments, über die der Server die einzelnen PageSegments wiederherstellen kann (FailOver). Dies bietet zusätzlich die Möglichkeit von stateless Servern, auch wenn es sinnvoll ist, die Benutzer immer auf den gleichen Server zu leiten, um deren Cache auszunutzen und das Dokument nicht immer neu holen und parsen zu müssen.

Zum jadice document platform 5 Dokumentenmodell siehe die dortige Dokumentation

Dokumente laden

Das Laden eines Dokuments wird im jadice web toolkit durch die Client-Anwendung initiiert. Folgend finden Sie eine grobe Beschreibung des Ablaufs beim Laden eines Dokuments:

Ladeablauf

  1. Zunächst erstellt der Client die Source-Objekte, die geladen werden sollen und teilt diese dann dem Reader mit. Ein Dokument kann hierbei auch aus mehreren Sources bestehen.

  2. Wurden alle Sources dem Reader übergeben, kann der Ladevorgang gestartet werden. Dies geschieht über den Aufruf der Methode Reader. complete. Die Dokument-Struktur (im Prinzip eine Reihe von Source Objekten) werden nun an den Server übermittelt.

  3. Der Server sucht zu jeder Source über die DocumentDataProviderRegistry jeweils passende DocumentDataProvider Implementationen, welche die Sources verarbeiten können.

  4. Die DocumentDataProvider Implementationen werden nun nacheinander aufgerufen und sind für das Laden der Seiten zuständig. Die Informationen, welche Daten genau geladen werden sollen, ist in den Source-Objekten spezifiziert, welche die DocumentDataProvider Implementationen als Parameter erhalten.

  5. Um ein späteres Wiederherstellen von Dokumenten zu ermöglichen, wird für eine Source auch ein PageSegmentHandle mit Hilfe des DocumentDataProviders erzeugt. Dieses PageSegmentHandle wird anschließend automatisch einer geladenen Seite zugewiesen.

  6. Das Dokument mit den PageSegments wird nun dem Client übergeben. Der Reader benachrichtigt die AsyncCallback; Implementation, welche beim Aufruf der Reader. complete -Methode übergeben wurde.

Wichtig: Zustand des erhaltenen Dokuments

Bei der Übergabe an die AsyncCallback; Implementation muss das Dokument noch nicht vollständig auf dem Client angekommen sein. Wenn möglich, werden die Seiten sukzessiv an den Client übermittelt, um möglichst frühzeitig die erste Seite eines Dokuments darstellen zu können. Der aktuelle Zustands eines Dokuments kann jederzeit über die Methode Document.getState überprüft werden.

Ladeablauf mit URIs

Das jadice web toolkit bringt bereits Implementierungen von Source und PageSegmentHandle für einen Ladevorgang mit URIs mit. Als Integrator muss dann lediglich ein entsprechender DocumentDataProvider bzw. konkret ein UriBasedDocumentDataProvider für das jeweilige Schema implementiert werden.

LazyLoading von zusammengesetzten Dokumenten

Mit dem jadice-Dokumentenmodell ist es möglich, ein logisches Dokument zu definieren, das aus mehreren physikalischen Dokumenten besteht. Lädt man ein solches Composite-Dokument nun, werden normalerweise alle Teildokumente geladen unabhängig davon, welche Teildokumente wirklich benötigt werden.

Um die Ladezeiten solcher Dokumente zu optimieren, ist es möglich, die verschiedenen Teildokumente jeweils erst dann zu laden, wenn diese benötigt werden. Lädt man ein solches Composite-Dokument, werden standardmäßig alle Teildokumente geladen. Mit der LazyLoading-Funktionalität kann das Laden auf die Teildokumente beschränkt werden, die gerade benötigt werden (z.B. zur Anzeige). Bei Funktionen, die das gesamte Dokument betreffen (z.B. die Textsuche oder der Export), werden alle restlichen Teildokumente nachgeladen.

Die LazyLoading-Funktionalität wird dadurch realisiert, dass zunächst ein LazyStreamPageSegment als Platzhalter für das eigentliche PageSegment eingefügt wird. Erst wenn dieser Platzhalter gerendert werden soll, werden die eigentlichen Inhalte geladen und gerendert.

Dazu muss durch den Integrator in der Implementierung des DocumentDataProvider sichergestellt werden, dass in der read()-Methode ein Dokument erzeugt wird, dessen Seiten LazyStreamPageSegments enthalten. Bei Erzeugung der LazyStreamPageSegments wird dabei definiert, woher der eigentliche Inhalt später gelesen wird. Weiterhin muss durch die Implementierung des PageSegmentHandle und der PageSegmentSource sichergestellt sein, dass auch bei Recovery einer nicht mehr im Cache vorgehaltenen Seite die zur Wiederherstellung benötigte Information im PageSegmentHandle vorgehalten wird.

Wird auf einem LazyStreamPageSegment dann zu einem späteren Zeitpunkt das Rendering ausgelöst, wird der eigentliche Inhalt dieses PageSegments gelesen und gerendert. Die Quelle des eigentlichen Inhalts muss bereits bei Erzeugung des LazyStreamPageSegment definiert werden.

Derzeit ist es im jadice web toolkit nicht vorgesehen, die Eigenschaften von Seiten - beispielsweise deren Dimension - nachträglich im Client zu aktualisieren. Deswegen muss die Größe der PageSegments bereits bei Erzeugung der LazyStreamPageSegment gesetzt werden. Weiterhin wird aufgrund der lazy geladenen Eigenschaften der PageSegments die Verwendung auflösungsabhängiger Annotationen nicht unterstüzt.

Die Verwendung des Lazy-Load-Mechanismus ist im showcase des jadice web toolkit beispielhaft implementiert. Dort wird im LazyClassPathDocumentDataProvider basierend auf LazyClassPathSource und einem dazugehörigen LazyClassPathHandle die Erzeugung eines Dokuments mit LazyStreamPageSegments als Platzhaltern gezeigt.

Wiederherstellung von Dokumenten

Die recover Methode wird aufgerufen, wenn im Servercache keine Dokumentstrukturinformation zu einem bereits geladenen Dokument gefunden werden kann.

Es gibt verschiedene Szenarien, die zu einem Fehlen des Dokumentes im Cache führen können:

  • Ein Benutzer öffnet ein Dokument und der Server wird, während dieses Dokument geöffnet ist, neu gestartet

  • Ein Benutzer öffnet ein Dokument. Anschließend fällt der zugehörige Server aus, und alle weiteren Anfragen des Benutzers werden durch LoadBalancing auf andere Server geleitet.

  • Ein Benutzer öffnet ein Dokument und dieses bleibt längere Zeit im Viewer geöffnet. In der Zwischenzeit werden viele weitere Dokumente von anderen Benutzern zur Anzeige gebracht. Durch den sich selbst organisierenden Servercache wird das schon länger serverseitig nicht angesprochene, aber immer noch geöffnete, Dokument aus dem Servercache entfernt.

Führt der Benutzer nun im besagten Dokument eine Aktion aus, wie z.B. das Scrollen zu einer anderen Seite, werden diese Dokumentinformationen, welche sich im Cache befanden, aber für den Rendervorgang der zugehörigen Seite benötigt. In diesem Fall wird die recover()-Methode des DocumentDataProviders aufgerufen.

Die Implementierung sieht im einfachen Fall analog zur read-Methode aus. Falls aber z.B. das Archivdokument im Rahmen der read-Methode serverseitig im Filesystem abgelegt wird, könnte die Implementierung der recover-Methode ohne einen weiteren Archivzugriff auskommen und versuchen, das Archivdokument direkt aus dem Filesystem einzulesen. Erst falls es dort nicht gefunden werden kann, muss ein erneuter Archivzugriff erfolgen.

Hierbei muss jedoch beachtet werden, dass in der recover()-Methode die PageSegmentHandles zur Verfügung stehen, nicht aber die Source. In den meisten Fällen können die Source-Informationen einfach auch in die Handles geschrieben werden, jedoch ist hier zu beachten, dass diese Handles in jedem PageSegment abgelegt werden und somit bei jeder Anfrage an den Server, die ein solches PageSegment betrifft, mit übertragen werden. Dies ist beispielsweise beim Rendern eines Tiles der Fall. In Spezialfällen kann es vorkommen, dass die Sourcen sehr groß werden, beispielsweise wenn direkt ein Datenstrom darin abgelegt wurde. Dann sollte dieser im DocumentDataProvider gesichert werden und nur eine Referenz darauf in das Handle geschrieben werden. Dies verringert die Größe der zwischen Client und Server übertragenen PageSegmentHandles, sodass die Netzwerkinfrastruktur entlastet wird.

Zusammengesetzte Dokumente

Zusammengesetzte Dokumente können nur serverseitig zusammengesetzt werden, damit die Recovery funktioniert. Ein clientseitiges Zusammensetzen ist aktuell nicht möglich. Das serverseitige Zusammensetzen kann mittels der Klassen CompositeDocumentDataProvider, CompositeSource und CompositeHandle erfolgen. Der nachfolgende Code zeigt die Verwendung des CompositeDocumentDataProviders. In diesem Beispiel werden drei Teildokumente zu einem zusammengesetzen Dokument zusammengefasst. Das zusammengesetzte Dokument beinhaltet hintereinander die drei Teildokumente. Mittels dieser Technik ist es möglich, beim Ladevorgang Seiten in einem Dokument aus verschiedenen Quellen zusammenzustellen. Es ist zu beachten, dass die append-Metode nur einmal aufgerufen werden darf, damit die Recovery gewährleistet ist. Hierzu sind die Teildokumente zunächst zu einem CompositeSource-Objekt zusammenzusetzen.

import com.levigo.jadice.web.client.reader.Reader;
import com.levigo.jadice.web.demo.common.server.dataprovider.CompositeDocumentDataProvider;
import com.levigo.jadice.web.demo.common.shared.service.sources.CompositeSource;

private AsyncCallbackk<Document> callback;

// create resources on client
private final static String[][] FILES_SERVER_COMPOSITE = new String[][]{
{
"Teildokument 1 (PDF)", "/Teildokument 1.pdf", "pdf"
}, {
"Teildokument 2 (TIFF)", "/Teildokument 2.tif", "image"
}, {
"Teildokument 3 (PDF)", "/Teildokument 3.pdf", "pdf"
},
};

List<String> sources = new ArrayList<>();
// add in reverse order
for (int i = FILES_SERVER_COMPOSITE.length - 1; i >= 0; i--) {
String[] file = FILES_SERVER_COMPOSITE[i];
sources.add(file[1]);
}

CompositeSource compositeSource = new CompositeSource(sources, "CompositeDoc (Server)");

Reader reader = new Reader();
reader.append(compositeSource); // important: only call append method once

// client finished creating CompositeSource and sends the list of documents to assemble to server
reader.complete(new ReaderEventTranslator(callback);

//server loads documents
compositeDocumentDataProvider.read(reader, resources);

Laden von hOCR-Daten

Die Verarbeitung und Verwendung von hOCR-Daten wird im jadice web toolkit unterstützt. Hierbei kann sich an der Dokumentation der jadice document platform 5, sowie dem Showcase orientiert werden.

Ausblenden von Seiten über FilteredDocuments

Mit Hilfe von FilteredDocuments ist möglich, Dokumentseiten auszublenden, um die Anzeige dieser Seiten zu unterdrücken. Dies kann für den Haupt-Viewer und die Thumbnail-Ansicht unabhängig voneinander geschehen. Eine ausführliche Beschreibung verschiedener Use Cases und deren Implementierung findet sich in der jadice knowledge base und verschiedenen Showcases.

Die APIs im Detail

Reader

Der Reader im Client besitzt die Methode append. append fügt einen neuen Abschnitt an die aktuelle Position ein. Wichtig ist, dass die append-Methode nur einmal aufgerufen werden darf, andernfalls entstehen Probleme bei der Recovery. Siehe Beispiel zu zusammengesetzten Dokumenten unter Zusammengesetzte Dokumente

Die Methode add wurde deprecated. Um ein Dokument auf ein anderes zu legen, kann stattdessen die serverseitige Implementierung genutzt werden. Vergleiche ClassPathWithAnnoDocumentDataProvider.read. Es ist wichtig, den targetIndex nach dem Lesen des Dokuments und vor dem Einlesen der Annotationen zurückzusetzen. Das Zurücksetzen erfolgt auf den Wert, den er vor dem Lesen des Dokuments innehatte.

DocumentDataProvider

Der DocumentDataProvider ist für den Zugriff auf die tatsächlichen Dokumentdaten und den Ladevorgang dieser verantwortlich.

Jeder DocumentDataProvider wird mit einer Source und einer PageSegmentHandle Klasse assoziiert. Diese Assoziation erfolgt über eine Registrierung des DocumentDataProvider bei der DocumentDataProviderRegistry.

Bei serverseitig zusammengesetzten Dokumenten ist es essentiell, dass in der Implementierung des DocumentDataProviders der Ziel-Seitenindex über Reader. setTargetIndex vor dem Lesen jedes Streams korrekt gesetzt wird, da sonst die Dokumente bzw. deren Annotationen falsch bzw. unvollständig zusammengesetzt werden.

UriBasedDocumentDataProvider

Ein spezieller DocumentDataProvider der für ein spezielles URI-Schema verantwortlich ist.

Bei der Verwendung eines eigenen URI-Schemas für das Laden von Dokumenten ist keine Implementierung von Source und PageSegmentHandle erforderlich. Dieser Weg ist sehr viel komfortabler als der Weg über Source und PageSegmentHandle, da hier weniger Code zu schreiben ist.

info

Das Angular-Frontend unterstützt lediglich das Laden von Dokumenten über URIs, nicht über die Kombination aus Source und PageSegmentHandle.

Beispiel: für das Laden eines Dokuments (mit der ID 1YQ4XR08) aus einem P8-Archiv würde der Lesevorgang folgendermaßen aussehen:

Angular-Frontend
    <jwt-multi-mode-viewer #viewerComponent
[source]="{uri: 'p8://1YQ4XR08'}"/>
GWT-Frontend
    Reader r = new Reader();
UriSource source = new UriSource("p8://1YQ4XR08", null);

r.read(source, new ReaderEventTranslator(new AsyncCallback<Document>() {...}
Backend
@Component
public class P8SchemeDataProvider implements UriBasedDocumentDataProvider {

private final String[] schemes = new String[] { "p8" };

public P8SchemeDataProvider() {
}

@Override
public void read(Reader reader, UriSource source) throws JadiceException, IOException {

}

@Override
public void recover(Reader reader, UriHandle handle) throws RecoverFailedException, JadiceException {

}

@Override
public boolean pertainsTo(String scheme) {
return P8SchemeDataProvider.super.pertainsTo(scheme);
}

@Override
public List<String> getSchemes() {
return Arrays.asList(schemes);
}
}

DocumentDataProviderRegistry

Um einen DocumentDataProvider für das jadice web toolkit zur Verfügung zu stellen, muss dieser bei der DocumentDataProviderRegistry registriert werden. Diese Registrierung wird über den WebtoolkitServerContext zur Verfügung gestellt.

Automatische Registrierung von DocumentDataProvidern

DocumentDataProvider-Implementierungen werden automatisch registriert, sofern diese mit @Component annotiert wurden. Siehe auch Automatische Registrierung von DocumentDataProvidern.

Übertragung user-spezifischer Dokumenten- und Seitenproperties

Userspezifische Properties vom Typ String, Integer und Boolean können zum einen vom Server an den Browser und zum anderen vom Browser an den Server übertragen werden. Hierbei wird zwischen dokumenten- und seitenbezogenen Properties unterschieden. Dokumentenbezogene Properties sind einem kompletten Dokument zugeordnet. Falls dies für einen bestimmten Anwendungsfall nicht feingranular genug sein sollte, können (zusätzlich) einer einzelnen Seite Properties hinzugefügt werden.

Der Transport der Properties erfolgt über eine Map , die im einen Fall dem zugehörigen Dokument und im anderen Fall der zugehörigen Seite zugeordnet ist. Das Setzen von Properties wird über die Methode com.levigo.jadice.document.Document#getProperties#put bzw. com.levigo.jadice.document.Page#getProperties#put realisiert. Analog dazu werden die Properties auf der Gegenseite über com.levigo.jadice.document.Document#getProperties#get bzw. com.levigo.jadice.document.Page#getProperties#get ausgelesen.

Das Hinzufügen und Ändern von Properties wird in Form von PropertyChangeEvents propagiert. Durch die Nutzung von DocumentListenern kann auf diese Fälle flexibel reagiert werden.