2 - Laden eines Dokuments

In 1 - Einbinden jadice web toolkit haben wir den jadice Viewer eingebunden, aber noch kein Dokument zur Anzeige gebracht. In diesem Tutorial legen wir die Basis zum Laden und zur Anzeige eines Dokuments von einer beliebigen HTTP-URL.

Um ein Dokument laden zu können, muss ein eigener DocumentDataProvider, eine passende Source und ein PageSegmentHandle implementiert werden. Damit JWT diese Implementierungen verwendet, müssen sie in der DocumentDataProviderRegistry registriert werden. Zur Anzeige eines Dokuments auf Client-Seite, muss dieser eine Source über einen Reader anfragen.

Das Ergebnis des Tutorials kann in https://github.com/levigo/jwt-getting-started/tree/master/jwt-tutorial-002 überprüft werden.

Projektstruktur

Folgende Klassen werden wir in diesem Tutorial ergänzen:

Verzeichnisstruktur
└── src/main/java/						
	└── org/jwt/
		├── shared/							- Hier legen wir Klassen ab, die sowohl auf Client, als auch auf Server-Seite benötigt werden.
		│	└── model/	 						
		│		├── UrlSource.java					- Unsere Source-Implementierung.	      
		│		└── UrlHandle.java					- Unsere PageSegmentHandle-Implementierung.	
		└── server/							- Hier legen wir Klassen ab, die nur auf Server-Seite benötigt werden.
			└── dataprovider/
			 	└── UrlDocumentDataProvider.java	- Unsere DocumentDataProvider-Implementierung

Source

Die Source enthält alle Informationen, die zum Auffinden des Dokument-Datenstroms benötigt werden. Das kann z.B. eine Dokument-Id innerhalb eines Archivs oder der Dokumenten-Pfad in einem Dateisystem sein. In unserem Beispiel ist das die HTTP-URL des Dokuments.

org.jadice.shared.model.UrlSource.java
package org.jadice.shared.model;

import org.jadice.server.dataprovider.UrlDocumentDataProvider;
import com.levigo.jadice.document.Document;
import com.levigo.jadice.web.shared.model.Source;

public class UrlSource extends Source {
    private static final long serialVersionUID = 1L;
    private String url;
    
	/** @deprecated NoArgsConstructor for Serialization. */
    public UrlSource() {    }

    public UrlSource(String url) {
        this.setUrl(url);
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    @Override
    public String toString() {
        return "UrlSource [url=" + url + "]";
    }
}

PageSegmentHandle

Wurde das Dokument vom DocumentDataProvider erfolgreich geladen, werden bereits geladene Seiten des Dokuments über ein PageSegmentHandle identifiziert. Alle weiteren Client-Anfragen zu einer Dokument-Seite werden automatisch über die PageSegmentHandles durchgeführt.

org.jadice.shared.model.UrlHandle.java
package org.jadice.shared.model;

import com.levigo.jadice.document.PageSegment;
import com.levigo.jadice.web.shared.model.PageSegmentHandle;

public class UrlHandle extends PageSegmentHandle {
    private static final long serialVersionUID = 1L;
    private String url;

    /** @deprecated NoArgsConstructor for Serialization. */
    public UrlHandle() {    }

    public UrlHandle(String url) {
        this.setUrl(url);
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    @Override
    public String getIdentifier() {
        return url;
    }

    @Override
    public PageSegmentHandle copy() {
        final UrlHandle h = new UrlHandle(url);
        copyTo(h);
        return h;
    }

    @Override
    public String toString() {
        return "UrlHandle [url=" + url + "]";
    }
}

DocumentDataProvider

Ein DocumentDataProvider ist auf Serverseite für das Laden von Dokumenten verantwortlich. Hier ist also der Einstiegspunkt zum Einbinden des eigenen Dokument-Archivs, Dokument-Dienstes oder einer beliebigen anderen Dokumenten-Ablage. In unserem Beispiel-Projekt laden wir die Resource, die über die von der Source übergebenen URL referenziert wird.

http://webtoolkit.jadice.com/doc/sect.chapter-reference.general.html#sect.reference.docdataprov

org.jadice.server.dataprovider.UrlDocumentDataProvider.java
package org.jadice.server.dataprovider;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import org.jadice.shared.model.UrlHandle;
import org.jadice.shared.model.UrlSource;
import com.levigo.jadice.document.JadiceException;
import com.levigo.jadice.document.read.Reader;
import com.levigo.jadice.web.server.DocumentDataProvider;
import com.levigo.jadice.web.shared.model.PageSegmentHandle;
import com.levigo.jadice.web.shared.model.Source;
import com.levigo.jadice.web.shared.service.exceptions.RecoverFailedException;

import org.springframework.stereotype.Component;

@Component
public class UrlDocumentDataProvider implements DocumentDataProvider<UrlSource, UrlHandle> {

 	public UrlDocumentDataProvider() {    }

    @Override
    public UrlHandle createSourceHandle(UrlSource source) {
        return new UrlHandle(source.getUrl());
    }

    @Override
    public void read(Reader reader, UrlSource source) throws JadiceException, IOException {
        InputStream documentStream = getResourceStream(source.getUrl());
        reader.read(documentStream);
    }

    @Override
    public void recover(Reader reader, UrlHandle handle) throws RecoverFailedException, JadiceException {
        try {
            InputStream documentStream = getResourceStream(handle.getUrl());
            reader.read(documentStream);
        } catch (IOException e) {
            throw new RecoverFailedException("Can't recover " + handle, e);
        }
    }

    private InputStream getResourceStream(String url) throws IOException {
        URLConnection connection = new URL(url).openConnection();
        return connection.getInputStream();
    }
}

Interessant sind vor allem folgende Methoden:

  • read()
    Dieser Aufruf wird beim initialen Laden des Dokuments durchgeführt. Der Client fordert über eine Source ein Dokument an, und die Implementierung versucht dieses anhand der enthaltenen Informationen zu laden.
  • recover()
    Falls ein bereits geladenes Dokument aufgrund technischer Probleme wiederhergestellt werden muss, passiert das über den revocer-Aufruf. Gut zu erkennen ist, dass hier die PageSegmentHandle als Referenz auf das entsprechende Dokument genutzt wird.

In beiden Fällen laden wir in getResourceStream() den Datenstrom anhand der übergebenen URL. Die Implementierung über URLConnection ist an dieser Stelle absichtlich so einfach gehalten. Eine produktive Implementierung sollte hier sicherlich mehr Wert auf Sicherheitsaspekte und Fehlerbehandlung legen.

Der DataProvider muss noch registriert werden. Hierzu verwenden wir den Ansatz über Spring-DI, bei Verwendung von Spring Boot (Handbuch - Spring-Integrationsmodul), sodass mit der oben verwendeten org.springframework.stereotype.Component  ein Auto-Wiring stattfindet und wir uns um nichts mehr kümmern müssen. 


Sollte sich GWT über nicht gefundene Klassen beschweren, müssen wir sicherstellen, dass

  1. die Anwendung korrekt kompiliert wurde (z.B. Maven > Update Maven Project oder Maven compile).
  2. das Package dieser Klasse als source in der gwt.xml gelistet ist.

Reader

Um im Client tatsächlich ein Dokument zu laden, müssen wir die gewünschte Source an einen Reader übergeben. Über einen AsyncCallback können wir im Erfolgsfall das zurückgelieferte Document zur Anzeige bringen, indem wir es der PageView unseres zuvor erstellten JadiceWidgets übergeben. In unserem Beispiel erweitern wir dazu die Klasse ApplicationEntryPoint:

org.jadice.client.ApplicationEntryPoint.java
(...)

import org.jadice.shared.model.UrlSource;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.levigo.jadice.document.Document;
import com.levigo.jadice.web.client.reader.Reader;

(...)

public class ApplicationEntryPoint implements EntryPoint {

	(...)

	@Override
    public void onModuleLoad() {

		(...)

		// finally load a testdocument
        loadDocument("https://www.levigo.de/fileadmin/download/jadicewebtoolkit.pdf");
 	}

	private void loadDocument(final String url) {
        Reader r = new Reader();
        r.append(new UrlSource(url));
        r.complete(new AsyncCallback<Document>() {
            @Override
            public void onSuccess(Document doc) {
                jadiceWidget.getPageView().setDocument(doc);
            }
            @Override
            public void onFailure(Throwable caught) {
                caught.printStackTrace();
                Window.alert("Cant load document from \"" + url + "\".");
            }
        });
    }
}

Modulkonfiguration (gwt.xml)

Unser UrlSource und PageSegmentHandle haben wir unterhalb des neuen packages org.jadice.shared abgelegt, das der Modulkonfiguration bisher nicht bekannt ist. Damit wir die Klassen auch auf Clientseite nutzen können, müssen wir unsere Modulkonfiguration noch anpassen:

src/main/java/org/jwt/Application.gwt.xml
<source path="shared" />

Zusammenfassung

In diesem Tutorial haben wir erfolgreich ein Dokument über eine HTTP-URL geladen. Wir können innerhalb des Dokument scrollen, weitere Interaktionsmöglichkeiten sind bisher aber nicht möglich.