3. Ressourcen
In einer typischen Webanwendung werden wir neben rein textuellen Informationen auch bestimmte Ressourcenarten, wie zum Beispiel Grafiken, Icons oder Datei-Downloads, einbinden wollen. Diese Daten können entweder aus unserer Anwendung selbst oder aus einer externen Quelle stammen. Vaadin unterstützt die Verwendung solcher Ressourcen über eine eigene API.
Ressourcen werden ganz allgemein über das Interface com.vaadin.server.Resource abgebildet. Dieses Interface steht an der Spitze der Vererbungshierarchie der verfügbaren Ressourcenarten von Vaadin. Eine Resource-Instanz repräsentiert ein Ressourcenobjekt, das auf verschiedene Weise in einer Vaadin-Anwendung eingebunden werden kann: als Link, als Bild, als Icon, als Download oder als eingebettetes Medienobjekt. Damit wird uns zum Beispiel die Darstellung eines Icons auf einer UI-Komponente, die Anzeige einer Bilddatei, das Einbetten eines YouTube-Videos oder einer Webseite in einem iFrame oder der Download einer dynamisch erzeugten Datei ermöglicht.
Vaadin bietet uns eine Reihe von Implementierungsklassen für das Resource-Interface. Diese erlauben es uns, Datenobjekte aus verschiedenen Quellen in unserer Anwendung einzubinden. Wir werden in diesem Kapitel die folgenden Ressourcenarten kennenlernen:
- URLs: Mit einer
com.vaadin.server.ExternalResourcelegen wir das Ziel eines Links oder die Adresse eines eingebundenen Medienobjekts fest. - Dateien innerhalb eines Vaadin Themes: Dateien, die innerhalb eines Themes abgelegt sind, können mithilfe einer
com.vaadin.server.ThemeResourceangesprochen werden. Themes werden wir in einem späteren Kapitel kennenlernen. - Dateien aus dem Dateisystem oder dem Klassenpfad werden mit den beiden Klassen
com.vaadin.server.FileResourceundcom.vaadin.server.ClassResourceeingebunden. - Dynamisch generierte Daten: Ressourcen können auch on-the-fly durch die Vaadin-Anwendung oder von einem beliebigen externen Prozess erzeugt werden. Dies geschieht über die Klasse
com.vaadin.server.StreamResource. -
Font Icons: Dies sind spezielle Schriftarten, deren Zeichen keine Buchstaben, sondern einzelne Piktogramme darstellen. Damit bieten Font Icons eine Alternative zur Verwendung von Bilddateien. Das Interface
com.vaadin.server.FontIconbildet die Grundlage für diese Art von Grafiken.
In diesem Kapitel wollen wir uns mit dem Einsatz solcher Ressourcen in einer Vaadin-Anwendung beschäftigen. Wir werden dazu die verschiedenen Ressourcenarten und die zu ihnen gehörenden Implementierungsklassen kennenlernen. Außerdem werden wir natürlich auch die Szenarien ansprechen, für die die einzelnen Ressourcenklassen eingesetzt werden können, und wir werden herausfinden, in welcher Situation eine bestimmte Ressourcenart am besten geeignet ist.
3.1 Verwendung von Ressourcen
Ressourcenobjekte können in einer Vaadin-Anwendung an verschiedenen Stellen eingesetzt werden. Sie werden dabei immer ganz allgemein über das Resource-Interface referenziert. So gibt man mit einer Resource das Ziel einer Link-Komponente an, oder man definiert mit ihr die Quelle eines com.vaadin.ui.Image-Objekts, oder man setzt die Adresse für ein BrowserFrame, einer Komponente, mit der sich ein iFrame in eine Anwendung einbetten lässt.
com.vaadin.ui.Link#setResource(Resource resource)
com.vaadin.ui.AbstractEmbedded#setSource(Resource source)
com.vaadin.ui.AbstractComponent#setIcon(Resource icon)
com.vaadin.ui.Notification#setIcon(Resource icon)
com.vaadin.ui.Video#Video(String caption, Resource source)
com.vaadin.ui.Video#setPoster(Resource poster)
com.vaadin.ui.BrowserFrame#BrowserFrame(String caption, Resource source)
Die Art und Weise, wie diese Ressource dann behandelt wird – das heißt ob ihre Daten auf der Programmoberfläche angezeigt werden oder ob sie zum Download angeboten wird – hängt von der Komponente ab, der man das Resource-Objekt übergibt. So stellt com.vaadin.ui.Link eine Ressource einfach in einem <a>-Tag als Hyperlink auf die Ressource dar, während die com.vaadin.server.FileDownloader-Extension die Ressourcendaten als Download bereitstellt 4.
Eine Ausnahme bei den Resource-Implementierungen bilden Font Icons. Diese können nur an einer einzigen Stelle, und zwar ausschließlich als Komponenten-Icons verwendet werden. Das heißt, sie dürfen lediglich als Parameter von Methoden Verwendung finden, mit denen sich ein Icon für eine Oberflächenkomponente setzen lässt. Ein Font Icon darf also insbesondere nicht als Quelle für ein Image-Objekt oder als Linkziel benutzt werden. Eine detailliertere Beschreibung von Font Icons finden Sie am Ende dieses Kapitels.
Ein Beispiel: Setzen eines Links
Bevor wir uns in die Details der verschiedenen Ressourcenarten vertiefen, wollen wir zunächst mit einem kleinen Beispiel die wohl am häufigsten verwendete Ressourcenklasse ausprobieren: Wir setzen einen einfachen Hyperlink, der auf die Vaadin-Homepage verweist.
ExternalResource address = new ExternalResource("http://www.vaadin.com");
Link vaadinHomepage = new Link("Zur Vaadin-Homepage", address);
layout.addComponent(vaadinHomepage);
Das war schon alles. Wir erzeugen ein Objekt vom Typ ExternalResource und initialisieren dieses mit der Adresse http://www.vaadin.com. Dieses Ressourcenobjekt übergeben wir als nächstes dem Konstruktor der Link-Komponente, die wir anschließend auf einem Layout platzieren. Im HTML-Code der Anwendung sehen wir dann das folgende Ergebnis:
<div class="v-link v-widget">
<a href="http://www.vaadin.com">
<span>Zur Vaadin-Homepage</span>
</a>
</div>
3.2 Das Resource-Interface
Die Hauptschnittstelle für Vaadin Ressourcen ist com.vaadin.server.Resource. Dieses Interface wird von jeder Ressourcenart implementiert. Eine Variable vom Typ Resource kann damit jede beliebige Ressourcenart repräsentieren. Durch ihren sehr allgemeinen Charakter definiert Resource kaum eigene Funktionalität. Es wird nur eine einzige Methode deklariert: getMIMEType() zum Festlegen des MIME Types einer Ressource. Die Bestimmung des konkreten MIME Types wird von Vaadin automatisch übernommen. Beim Zugriff auf eine Ressource wird diese Information immer mitgeschickt und vom Browser entsprechend ausgewertet.
Das Resource-Interface wird von den beiden Klassen ExternalResource und ThemeResource direkt implementiert. Wir werden diese beiden Ressourcenarten weiter unten noch genauer kennenlernen. Daneben gibt es noch die sogenannten Connector-Ressourcen.
3.3 Connector-Ressourcen
Direkt von Resource abgeleitet ist die Schnittstelle com.vaadin.server.ConnectorResource. Eine Besonderheit von Connector-Ressourcen ist, dass ihre Daten über einen sogenannten Connector bereitgestellt werden. Um das zu verstehen, müssen wir erst einmal wissen, was ein Connector ist. Ein Connector ist derjenige Teil einer UI-Komponente, der für ihre Kommunikation zwischen Client und Server zuständig ist. Jede Vaadin-Komponente hat ihren eigenen Connector. Damit steht eine Connector-Ressource immer in einer engen Beziehung zu einer bestimmten UI-Komponenteninstanz.
Mit Connector-Ressourcen können Daten bereitgestellt werden, die aus der Anwendung selbst stammen und dabei keinen eigenen, vordefinierten URI mitbringen. Dies können zum Beispiel Daten sein, die erst bei Abruf durch den Browser erzeugt werden.
Die Schnittstelle ConnectorResource deklariert daher auch zwei für diesen Zweck passende Methoden: getStream() und getFilename(). Während getFilename() den Dateinamen festlegt, unter dem der Browser die Ressourcendaten herunterladen soll, liefert getStream() die Daten selbst. Diese Methode gibt ein Objekt vom Typ com.vaadin.server.DownloadStream zurück. Dieses enthält alle zu einer Connector-Ressource gehörenden Informationen, darunter unter anderem ein InputStream, der die Ressourcendaten selbst enthält.
Implementiert wird das Interface ConnectorResource von den Klassen ClassResource, FileResource und StreamResource. Während bei ClassResource und FileResource die Ressourcendaten aus dem Klassenpfad bzw. aus dem Dateisystem stammen, haben wir mit einer StreamResource die Möglichkeit, den InputStream, der über das DownloadStream-Objekt zurückgegeben wird, selbst zu definieren. Damit können wir also dynamisch generierte Daten, wie zum Beispiel Reports oder Datenexporte, bereitstellen.
Wie weiter oben schon erwähnt, sind dies alles Ressourcen, deren Daten aus der Anwendung stammen und die keinen eigenen URI besitzen. Weiter wissen wir, dass man ein beliebiges Resource-Objekt zum Beispiel als Zielressource in eine Link-Komponente stecken kann. Hierbei haben wir aber scheinbar einen Konflikt, denn wie kann ich eine Ressource ohne eigenen URI als Ziel eines Links angeben, wenn dieses Ziel doch ein URI sein muss? Wie kann aber eine dynamisch generierte oder aus dem Klassenpfad gelesene Ressource mit einem eindeutigen URI adressiert werden? Die Daten liegen ja nicht als durch den Webserver zugreifbare Dateien in einem Dokumentenverzeichnis. Dieser Frage wollen wir im folgenden Abschnitt nachgehen.
Adressierung von Connector-Ressourcen
Connector-Ressourcen besitzen von Haus aus keinen eigenen URI, unter dem sie abgerufen werden könnten. Sie liegen nicht wie normale Dateien im Document Root eines Webservers, sondern werden vom Vaadin-Servlet direkt bereitgestellt und übertragen. Trotzdem kann man Connector-Ressourcen überall dort verwenden, wo ein URI erwartet wird. Zum Beispiel kann man eine Connector-Ressource, die dynamisch erzeugte Inhalte bereitstellt, in ein Link-Objekt setzen. Wie passt das zusammen?
Hier kommt der Connector ins Spiel, der namensgebend für diese Ressourcenart ist. Jede UI-Komponenteninstanz, die auf einer Benutzeroberfläche platziert ist, wird durch ein eigenes Connector-Objekt repräsentiert. Jedes dieser Objekte hat eine eindeutige Id, die von Vaadin intern verwaltet wird. Eine UI-Komponenteninstanz, die auf einer Benutzeroberfläche liegt, befindet sich immer im Inneren einer Komponentenhierarchie unterhalb eines UI-Objekts. Wie wir wissen, können sämtliche UI-Instanzen einer HTTP-Session über deren UI Id identifiziert werden. Zusammen mit dieser UI Id und einer Connector-Id kann also eine ganz bestimmte UI-Komponenteninstanz innerhalb einer Benutzersession eindeutig adressiert werden.
Mithilfe dieser Adressierung kann Vaadin nun für Connector-Ressourcen einen künstlichen URI erzeugen. Der generierte URI beinhaltet neben dem Dateinamen der Ressource (wird durch getFilename() geliefert) die Connector-Id der UI-Komponenteninstanz und die Id des UI-Objekts, in dem sich die Instanz befindet. Diese Connector-URIs werden durch das Vaadin-Servlet interpretiert und behandelt. Damit das Vaadin-Servlet weiß, welche Ressourcendaten durch eine bestimmte Komponente bereitgestellt werden sollen, muss es das dazugehörige Resource-Objekt kennen. Anhand des Connector-URIs kann sich das Vaadin-Servlet dieses Objekt gezielt aus der HTTP-Session fischen. Dadurch wird es möglich, auch dynamisch generierte Ressourcen eindeutig zu adressieren.
Schauen wir uns ein einfaches Beispiel dazu an. Wir wollen eine Datei Readme.txt, die im lokalen Dateisystem liegt, mithilfe eines Links zum Download anbieten. Dazu verwenden wir eine FileResource und die Link-Komponente.
1 FileResource fileResource = new FileResource(new File("C:/readme.txt"));
2 Link link = new Link("Download readme.txt", fileResource);
3 mainLayout.addComponent(link);
Wenn wir jetzt den HTML-Code betrachten, der durch dieses Beispiel erzeugt wird, sehen wir folgendes Ergebnis:
<div class="v-link v-widget">
<a href="http://localhost:8080/APP/connector/1/19/href/readme.txt">
<span>Download readme.txt</span>
</a>
</div>
Hier bekommen wir den künstlichen URI
http://localhost:8080/APP/connector/1/19/href/readme.txt
Dieser enthält die Id 1 des aktuellen UI-Objekts und die Id 19 des Connectors für das Link-Objekt. Anhand dieser Information kann das Vaadin-Servlet das Link-Objekt aus der HTTP-Session ermitteln und dessen FileResource-Objekt auslesen. Mit der Information aus dieser Dateiressource kann Vaadin nun den Inhalt von readme.txt als Download ausliefern.
3.4 Implementierungsklassen von Resource
Es ist nun an der Zeit, sich die verschiedenen Ressourcenimplementierungen ein wenig genauer anzuschauen.
ExternalResource
Die einfachste Ressourcenart haben wir am Anfang schon in einem Beispiel gesehen. Wir verwenden ExternalResource, um eine externe Ressource über deren URI zu definieren. Die Klasse erwartet eine gültige URL als Konstruktorargument. Diese kann entweder als String oder als Objekt vom Typ java.net.URL übergeben werden.
Neben der Definition der Zieladresse für einen Link ist ein weiterer typischer Verwendungszweck von ExternalResource die Angabe der Quelle eines com.vaadin.ui.Image-Objekts. Mit der Image-Komponente lässt sich ein Bild auf einer Seite anzeigen.
ThemeResource
Die Ressourcenart ThemeResource ermöglicht das Einbinden von Dateien, die unterhalb eines Vaadin Themes abgelegt sind. Als Theme wird die Anpassung der optischen Gestaltung einer Vaadin-Anwendung mithilfe von Cascading Style Sheets und Bilddateien bezeichnet. Wie wir in dem Kapitel über Themes noch sehen werden, besteht ein Theme aus einem speziellen Wurzelverzeichnis, dem weitere Dateien und Verzeichnisse untergeordnet sind. Unter anderem befinden sich darin CSS-Dateien, die das Erscheinungsbild einer Vaadin-Anwendung anpassen. Man kann daneben aber auch weitere Ressourcen ablegen, wie zum Beispiel Bilder, Icons oder andere Mediendateien. Mithilfe einer ThemeResource kann eine solche Datei immer in Bezug auf das aktuell verwendete Theme referenziert werden.
Das Besondere an Theme-Ressourcen ist, dass erst zur Laufzeit abhängig vom aktuell verwendeten Theme bestimmt wird, welche konkrete Datei von der Ressource tatsächlich referenziert wird. Wechselt man das Theme, werden dadurch von den Theme-Ressourcen auch andere Dateien referenziert. Theme-Ressourcen werden immer über relative Pfadangaben definiert, die sich auf das aktive Theme beziehen.
Theme-Ressourcen werden in erster Linie für Bilddateien verwendet, die vom aktiven Theme abhängig sein sollen, also zum Beispiel Icons, Logos oder grafische Gestaltungselemente. Beispielsweise kann man damit theme-abhängige Icon-Sets definieren. Wenn das aktuelle Theme gewechselt wird, sollen dadurch natürlich auch die von der Anwendung dargestellten Icons ausgetauscht werden, sodass ein zur Optik des Themes passendes Icon-Set verwendet wird.
Mit dieser Fähigkeit lassen sich sehr leicht mandantenfähige Anwendungen erstellen, die sich in ihrem Erscheinungsbild an die jeweiligen Mandanten anpassen können, ohne dass für neue Mandanten der Quelltext der Anwendung angefasst werden müsste. Eine solche Anwendung kann dann für verschiedene Kunden oder Anwenderkreise unterschiedlich aussehen.
Stellen Sie sich vor, Sie hätten zwei für Ihre Kunden zugeschnittene Themes AcmeCorpTheme und InitechTheme. Beide Themes enthalten in einem Unterverzeichnis img jeweils eine Grafik companyLogo.png, welche das Firmenlogo des jeweiligen Kunden darstellt. Das folgende Schaubild zeigt die Verzeichnisstruktur, die sich für die beiden Themes in Ihrem Projekt ergibt.
VAADIN/themes-VerzeichnissesSie können dieses Logo wie folgt auf Ihrer Programmoberfläche einbinden:
ThemeResource logoResource = new ThemeResource("img/companyLogo.png");
Image image = new Image("", logoResource);
layout.addComponent(image);
Wie Sie sehen, müssen wir hier keine explizite Referenz auf ein bestimmtes Theme angeben. Wir spezifizieren lediglich einen relativen Pfad, dessen Wurzel sich immer auf das Basisverzeichnis des aktiven Themes bezieht. Ist z. B. das Theme für den Kunden Initech aktiv, wird auch das Firmenlogo aus diesem Theme angezeigt. Damit stellen wir sicher, dass für jeden Kunden immer das passende Logo angezeigt wird.
FileResource
Mit einer FileResource lässt sich eine beliebige Datei aus dem lokalen Dateisystem als Ressource einbinden. Dabei spielt es keine Rolle, ob die Datei im Kontext (im Document Root) der Webanwendung liegt. Die einzige Voraussetzung ist, dass die Datei durch den System-User lesbar ist, mit dem der Web Server betrieben wird, und dass der Java Security Manager den Zugriff auf das lokale Dateisystem zulässt.
Initialisiert wird eine FileResource mit einem java.io.File-Objekt. Das folgende Beispiel fügt ein PDF-Dokument aus einem lokalen Verzeichnis in eine Anwendung ein.
1 File file = new File("/home/rkrueger/documents/agb.pdf");
2 FileResource resource = new FileResource(file);
3 Link link = new Link("AGB herunterladen", resource);
4 layout.addComponent(link);
Die Einsatzmöglichkeiten von Dateiressourcen sind relativ beschränkt. Aus Sicherheitsgründen rate ich nach Möglichkeit von der Verwendung dieser Ressourcenart ab. Insbesondere sollten Dateiressourcen nicht zur Auslieferung von Standardressourcen einer Webanwendung verwendet werden. Dazu gehören eingebettete Bilder und Icons. Einer der Gründe hierfür liegt darin, dass diese Ressourcen durch die künstlichen und veränderlichen URIs von Connector-Ressourcen nur schlecht vom Browser gecacht werden können. Je nach Anwendung wird der Browser dieselbe Ressource unter Umständen mehrmals unter verschiedenen URIs cachen. Verwenden Sie für solche Ressourcen möglichst einen dedizierten Web Server, der die Daten unter festen URIs ausliefert. Die oben kennengelernte Klasse ExternalResource ist dann der richtige Kandidat.
ClassResource
Ressourcen vom Typ ClassResource beziehen ihre Daten aus dem Klassenpfad der Anwendung. Bei einer Webanwendung setzt sich der Klassenpfad aus verschiedenen Orten zusammen. Dazu gehört das Verzeichnis WEB-INF/classes und WEB-INF/lib innerhalb des Deployment-Verzeichnisses der Webanwendung selbst. Dateien, die an diesen Orten abliegen, können mit einer ClassResource referenziert werden.
Um die Daten einer Ressource aus dem Klassenpfad auszulesen, holen sich ClassResource-Objekte über die Methode java.lang.Class#getResourceAsStream() eines bestimmten Klassenobjekts einen InputStream auf die referenzierte Datei. Damit wird auf die Ressource über denjenigen Classloader zugegriffen, der das angegebene Klassenobjekt geladen hat. Zum Laden einer ClassResource ist also immer die Angabe eines Klassenobjekts notwendig. Die Klasse bietet daher die folgenden beiden Konstruktoren an:
ClassResource(Class<?> associatedClass, String resourceName)ClassResource(String resourceName)
Mit dem ersten Konstruktor wird das Klassenobjekt explizit vorgegeben, über deren Classloader die Ressource geladen werden soll. Der zweite Konstruktor verwendet standardmäßig die UI-Klasse der Anwendung als Referenzklasse.
Wurde die Referenzklasse aus einer bestimmten Jar-Datei geladen, so können mit einer ClassResource Dateien eingebunden werden, die aus dieser Jar-Datei stammen.
Wenn Sie Maven als Buildtool verwenden, können Sie Ihre Klassenpfadressourcen im Verzeichnis src/main/resources/ ablegen. Dateien aus diesem Verzeichnis werden in den Klassenpfad der Anwendung (nach WEB-INF/classes) kopiert. Beispielsweise können Sie die Bilddatei src/main/resources/logo.gif mit der folgenden ClassResource referenzieren:
Resource logo = new ClassResource("/logo.gif");
Die Verwendung von Klassenpfadressourcen bietet sich vor allem dann an, wenn die anderen Ressourcenarten nicht oder nur eingeschränkt zur Verfügung stehen. Hat man zum Beispiel keinen Zugriff auf das Dateisystem oder es steht kein externer Webserver zur Verfügung, der die statischen Ressourcen einer Anwendung ausliefern kann, bleiben einem nur noch ClassResource oder ThemeResource zum Einbinden von Ressourcen.
Das kann beispielsweise dann der Fall sein, wenn man seine Anwendung in der Google App Engine betreibt. Dort hat eine Anwendung nur eingeschränkten Zugriff auf ihre Umgebung. So ist zum Beispiel der Zugriff auf ein lokales Dateisystem nicht möglich. Alle Ressourcen, auf die eine Anwendung zugreifen möchte, müssen deshalb innerhalb der Anwendung selbst liegen und mit ihr ausgeliefert werden. Hier bietet es sich besonders an, die Ressourcen in den Klassenpfad der Anwendung zu legen und mit ClassResource-Objekten auf diese zuzugreifen.
StreamResource
Mit Ressourcen vom Typ StreamResource können wir dynamisch generierte Daten bereitstellen. Mit ihnen haben wir die Möglichkeit, die Ressourcendaten selbst und bei Bedarf zu erzeugen. Um dies zu erreichen, müssen wir uns selbst um die Erzeugung der Daten kümmern, die von dem InputStream des Ressourcenobjekts geliefert werden.
Die Klasse StreamResource erlaubt uns damit, dynamisch generierte Ressourcen, wie zum Beispiel Datenexporte oder ad-hoc erzeugte Grafiken, zu verwenden.
Um eine StreamResource zu erstellen, müssen wir uns um zwei Dinge kümmern. Zum einen müssen wir den Dateinamen angeben, unter dem die generierte Ressource angesprochen werden soll. Zum anderen müssen wir die Daten der Ressource selbst erzeugen. Die Daten werden von einer Klasse geliefert, die das Interface StreamResource.StreamSource implementiert.
Dieses Interface ist wie folgt definiert:
1 public interface StreamSource extends Serializable {
2 public InputStream getStream();
3 }
StreamSource ist also eine Fabrikschnittstelle zur Erzeugung von InputStreams, die über getStream() die Daten der dynamisch erzeugten Ressource liefert.
StreamSource als FabrikschnittstelleVerwendet wird eine Instanz von StreamSource dann als Konstruktorargument für StreamResource:
com.vaadin.server.StreamResource#StreamResource(StreamSource streamSource,
String filename)
Wollen wir mit StreamResource bei jeder Anforderung durch den Browser die Ressourcendaten neu generieren lassen, müssen wir beachten, dass der Browser eine einmal erzeugte StreamResource im Cache behält und weitere Anforderungen der Ressource daraus bedient werden. Soll eine StreamResource wirklich jedes Mal neu erzeugt werden, können wir mit StreamResource#setCacheTime(0) die Zeitdauer, die die Ressource im Browser-Cache liegen darf, auf 0 Millisekunden festlegen.
Schauen wir uns ein Beispiel für eine StreamResource an. Um unsere Anwender vor Spammern zu schützen, die automatisiert Email-Adressen von Webseiten fischen, wollen wir alle Email-Adressen, die irgendwo angezeigt werden, generell als Grafik darstellen. Die Adressen erscheinen dann nicht im HTML-Quelltext, und wir zwingen unsere Anwender damit, bei Bedarf die Email-Adressen abzutippen. Wir müssen dazu Strings in Bilder umwandeln können. Dies erreichen wir mit der Klasse EmailImageResource, die von StreamResource abgeleitet ist.
1 public class EmailImageResource extends StreamResource {
2 public EmailImageResource(String emailAddress, String filename) {
3 super(new EmailImageSource(emailAddress), filename);
4 }
5 }
Wie Sie sehen, ist diese Klasse relativ unspektakulär. Wir rufen einfach nur den Super-Konstruktor auf und übergeben eine Instanz unserer eigenen Implementierung der StreamSource-Schnittstelle. Diese sieht wie folgt aus:
1 public class EmailImageSource implements StreamResource.StreamSource {
2 private String emailAddress;
3
4 public EmailImageSource(String emailAddress) {
5 this.emailAddress = emailAddress; // {1} //
6 }
7
8 @Override
9 public InputStream getStream() {
10 BufferedImage image = new BufferedImage(
11 125, 30, BufferedImage.TYPE_3BYTE_BGR); // {2} //
12 Graphics graphics = image.getGraphics();
13 graphics.setColor(Color.white);
14 graphics.fillRect(0, 0, 125, 30);
15 graphics.setColor(Color.black);
16 graphics.drawString(emailAddress, 10, 20); // {3} //
17 try {
18 ByteArrayOutputStream buffer = new ByteArrayOutputStream();
19 ImageIO.write(image, "png", buffer);
20 return new ByteArrayInputStream(
21 buffer.toByteArray()); // {4} //
22 } catch (IOException e) {
23 e.printStackTrace(); // {5} //
24 return null;
25 }
26 }
27 }
Hier merken wir uns die über den Konstruktor mitgegebene Email-Adresse, damit wir diesen Text später bei Bedarf in ein Bild einfügen können {1}. Interessant ist hier die Implementierung von getStream(). Diese Methode soll ja den InputStream liefern, der die Daten unserer Ressource enthält. Wir erzeugen uns daher mit den Klassen aus dem java.awt-Package ein BufferedImage-Objekt, mit dem wir programmatisch ein Bild erzeugen können {2}. Auf dieses Bild zeichnen wir die als Konstruktorargument übergebene Email-Adresse {3}. Wenn das Bild fertig konfiguriert ist, können wir es in einen ByteArrayInputStream umwandeln und diesen als Rückgabewert von getStream() zurückliefern {4}. Der Umgang mit Exceptions sollte natürlich in einer echten Anwendung etwas eleganter gelöst werden, als in diesem Beispiel {5}.
Unsere eigene Ressourcenimplementierung EmailImageResource kann nun als Datenquelle für ein Image-Objekt verwendet werden:
1 Image emailImage = new Image(
2 "Erreichen Sie uns unter folgender Email-Adresse:",
3 new EmailImageResource("info@example.com", "email.png"));
4 mainLayout.addComponent(emailImage);
Das Ergebnis sieht auf der Programmoberfläche dann wie folgt aus:
Sie finden den vollständigen Code von diesem Beispiel im Modul Kap10.4_StreamResource in den Maven-Beispielprojekten zu diesem Buch.
FontIcon und FontAwesome
Über das Interface FontIcon haben wir in unserer Anwendung die Möglichkeit, Font Icons für Icon-Ressourcen zu verwenden. Font Icons sind eine ressourcenschonende Alternative zu Grafikdateien.
Herkömmliche Icons, die über Bilddateien eingebunden werden, haben zwei wesentliche Nachteile: Icons bestehen üblicherweise aus kleinen Dateien, die einzeln vom Server geladen werden müssen. Dieser Ladevorgang kann bei einer großen Anzahl von Icons den Seitenaufbau spürbar verlangsamen. Dies ist unter anderem der Tatsache geschuldet, dass ein Browser immer nur eine bestimmte, fest vorgegebene Anzahl von Ressourcen gleichzeitig vom Webserver nachladen kann. Die maximal erlaubte Anzahl gleichzeitig geöffneter Server-Verbindungen ist von Browser zu Browser unterschiedlich, bewegt sich aber bei jedem Browser-Typ im niedrigen zweistelligen Bereich.
Ein weiterer Nachteil von Bilddateien ist, dass diese nicht skalierbar sind. Es handelt sich hierbei eben um Rastergrafiken, die man ohne Qualitätsverlust nicht beliebig vergrößern kann. Auch ist ihre Farbgebung fest vorgegeben.
Font Icons verfolgen einen anderen Ansatz. Ein Satz von Font Icons wird durch eine spezielle Schriftart definiert. Eine solche Schriftart setzt sich nicht aus Buchstaben zusammen, sondern aus Piktogrammen — dies ist vergleichbar mit der Windows-Schriftart Webdings. Jeder Buchstabe dieser Schriftart stellt damit ein bestimmtes Symbol dar.
Dieser Ansatz hat den Vorteil, dass zum einen der gesamte Satz aller verfügbaren Icons einer Anwendung mit einem Mal (nämlich beim Laden der Schriftart) vom Server geholt wird. Zum anderen sind Font Icons beliebig und ohne Qualitätsverlust skalierbar — Schriftarten bestehen eben aus Vektorgrafiken. Der einzige Nachteil von Font Icons: man kann sie immer nur einfarbig darstellen. Dafür können Font Icons aber mit allen Mitteln, die einem Cascading Style Sheets bieten, beliebig gestaltet werden. Somit kann man die gleichen Icons in verschiedener Darstellung verwenden: schattiert, rotiert, skaliert, durchsichtig, mit Leuchteffekt, usw. — nur eben einfarbig.
FontIcon ist nur eine Schnittstelle zur Definition eigener Font Icon Sets. Wenn wir Font Icons in unserer Anwendung verwenden wollen, müssen wir eine Implementierungsklasse von diesem Interface verwenden. Vaadin bringt von Hause aus die Icons des Font Awesome-Projekts mit, die über die Enum-Klasse com.vaadin.server.FontAwesome verfügbar sind. Diese Klasse definiert eine lange Liste von Enum-Konstanten, von denen jede ein bestimmtes Font Icon repräsentiert.
Wir können diese Konstanten zum Setzen von Komponenten-Icons verwenden, also immer im Zusammenhang mit der Methode com.vaadin.ui.Component#setIcon() (neben einigen anderen Stellen im Vaadin-Framework, wo wir ein Icon setzen können).
Wir können zum Beispiel eine Schaltfläche mit einem Font Icon aufwerten:
1 Button okButton = new Button("Ok");
2 okButton.setIcon(FontAwesome.CHECK);
Im Ergebnis sieht diese Schaltfläche wie folgt aus:
Der FontAwesome Icon Font ist automatisch in Vaadins Valo Theme eingebunden. Möchte man FontAwesome (oder einen anderen Icon Font) mit einem anderen Theme verwenden, so muss dies explizit für das Theme aktiviert werden. Wie das funktioniert, werden wir uns in dem Kapitel über Vaadin Themes genauer anschauen.
3.5 Zusammenfassung
Dieses Kapitel hat uns mit dem Ressourcen-Mechanismus von Vaadin bekannt gemacht. Ressourcen sind für Vaadin Daten, die von einer Anwendung eingebunden und angezeigt werden können. Das können Bilder, Icons oder beliebige andere Datei-Downloads sein. Die Daten können entweder von der Anwendung selbst zur Verfügung gestellt werden oder sie stammen aus einer externen Quelle.
Ressourcen können von unterschiedlichen UI-Komponenten dargestellt werden. Der häufigste Fall wird die Verwendung einer ExternalResource zusammen mit einer Link-Komponente sein, um einen Hyperlink zu erhalten. Ressourcen können aber auch als Grafiken oder andere Medienobjekte eingebunden und als Piktogramm, Bild-, Video- oder Audio-Datei verwendet werden.
Vaadin stellt uns einige Ressourcenimplementierungen zur Verfügung, die ihre Daten aus unterschiedlichen Quellen beziehen. Hier haben wir ExternalResource zur Adressierung einer Ressource über eine URL und ThemeResource für Ressourcen aus einem Vaadin Theme kennengelernt.
Weiter haben wir Bekanntschaft mit Connector-Ressourcen gemacht, eine Klasse von Ressourcen, die aus der Vaadin-Anwendung selbst stammen und die keinen eigenen URI haben. Damit diese Art von Ressourcen dennoch bspw. zusammen mit einem Link-Objekt verwendet werden können, kümmert sich Vaadin um die Erzeugung eines künstlichen URIs für diese Ressourcen. In diesem URI steckt die Connector-Id der UI-Komponente und die UI Id der aktuellen UI-Instanz.
Vaadin bietet uns drei verschiedene Connector-Ressourcenarten an. Mit FileResource können wir eine beliebige Datei aus dem lokalen Dateisystem als Ressource einbinden. ClassResource erlaubt uns den Zugriff auf Ressourcen, die im Klassenpfad einer Anwendung liegen. Und StreamResource ermöglicht uns die dynamische Generierung der Ressourcendaten bei Bedarf, indem wir selbst den InputStream der Ressource erzeugen.
Als letzte Ressourcenart haben wir die Font Icons kennengelernt. Font Icons bestehen aus einer speziellen Schriftart, deren Buchstaben einzelne Piktogramme darstellen. Damit sind Font Icons wesentlich besser skalierbar und performanter als einzelne Bilddateien, können aber immer nur mit einer Vordergrund- und einer Hintergrundfarbe dargestellt werden. Vaadin liefert über das Valo-Theme die Font Icons des Font Awesome-Projekts mit, auf die wir mit der Enum-Klasse FontAwesome zugreifen können.