15. Oktober 2014 | 4 min lesezeit

Multi-Browser-Tab-Support in Java EE 7

Webentwickler kennen das Problem. Bei der Umsetzung bestimmter Use Cases (z. B. Wizards) ist es notwendig, Daten über mehrere Requests hinweg vorzuhalten. Vor Java EE 6 gab es da nur die Möglichkeit, die Daten @SessionScoped abzulegen. Wenn aber der Benutzer die Webanwendung in zwei oder mehr Browsertabs oder -Fenstern verwendet, werden diese Daten zwischen beiden Tabs oder Fenstern geteilt, was meist zu unerwünschten Effekten führt.

Der mit JSF 2.0 neu hinzugekommene View Scope (den es seit JSF 2.2 auch als CDI Scope gibt) löst das Problem, solange man sich auf einer XHTML-Seite befindet. Möchte man aber Daten (innerhalb eines Tabs) über mehrere Seiten hinweg behalten und dabei nicht auf Multi-Browser-Tab-Support verzichten, muss man zu anderen Mitteln greifen. Wir werden in dieser Kolumne betrachten, welche Möglichkeiten es da gibt und worauf man dabei achten muss.

Die erste Lösungsmöglichkeit für das Problem, Daten zwar über mehrere Seiten hinweg vorzuhalten, aber sie dennoch nicht in die Session zu legen, kam mit Java EE 6 und CDI, und zwar mit dem Conversation Scope. Die Idee ist einfach: Über ein Objekt namens Conversation, das man sich injizieren lassen kann, kann gesteuert werden, wann der Lebenszyklus der @ConversationScoped Beans beginnt und wann er endet (Listing 1). Daten in @ConversationScoped Beans können dann mehrere Requests überleben, müssen aber nicht die gesamte Session vorgehalten werden. Es handelt sich also um einen Scope, der länger als ein Request (und auch länger als eine View) andauert, aber kürzer als eine Session.

Technisch wird die Conversation über einen Request-Parameter namens cid gesteuert. Dieser gibt an, in welcher Conversation sich ein Request befindet. Auf dieser Basis können dann die jeweils richtigen Bean-Instanzen ausgewählt werden. Mit dem Conversation Scope kann es dem Benutzer tatsächlich ermöglicht werden, mit derselben Anwendung in mehreren Browsertabs parallel zu arbeiten und dabei auf unterschiedliche Daten zuzugreifen. Es wird in jedem Tab eine eigene Conversation gestartet, und der Server kann über die cid identifizieren, von welchem Tab ein Request gerade kommt.

Listing1

Das Client-Window

Die Idee, über einen Request-Parameter den aktuellen Browsertab bzw. das aktuelle Browserfenster zu identifizieren, wurde in JSF 2.2 aufgegriffen. Es wurde das Konzept des Client-Windows eingeführt. In der Applikation kann darüber identifiziert werden, aus welchem Client-Window (aka Browsertab oder Browserfenster) heraus der aktuelle Request ausgeführt wird. Auf das Client-Window kann über die neu eingeführte Methode getClientWindow() des ExternalContexts zugegriffen werden, der vom FacesContext geholt werden kann. Identifiziert wird das Client-Window genau wie die Conversation über einen Request-Parameter. In diesem Fall heißt er jfwid. Er wird automatisch zu jedem Link auf einer JSF-Seite hinzugerendert. Zusätzlich wird für POST-Requests ein Hidden-Input gerendert, der auch die Window-ID enthält.

Um abwärtskompatibel zu bleiben, ist das Feature des Client-Windows aber per Default ausgeschaltet und muss in der web.xml über den Contextparameter javax.faces.CLIENT_WINDOW_MODE aktiviert werden. Laut JSF-Spec müssen hier die Werte none und url unterstützt werden. Es steht JSF-Implementierungen aber frei, weitere Werte zu unterstützen.

Öffnen in neuem Tab

So gut die Identifikation des Client-Windows über einen Request-Parameter auch funktioniert, hat sie doch einen Nachteil: Möchte man aus einem Tab heraus einen neuen Tab öffnen, wird der Parameter mit übertragen, und beide Tabs teilen sich eine Client-Window-ID, was gegen das Vorhaben, Browsertabs darüber identifizieren zu können, verstößt.

Entscheidet man sich bereits beim Page-Design dazu, dass ein bestimmter Link in einem neuen Tab geöffnet werden soll, so hilft ein Attribut weiter, das in JSF 2.2 neu zu h:link und h:button hinzugekommen ist. Es heißt disableClientWindow. Ist es auf true gesetzt, wird die jfwid nicht an den Link gerendert:

Kompliziert wird das Client-Window-Handling allerdings, wenn nicht der Page-Designer, sondern der Anwender auf die Idee kommt, einen Link in einem neuen Tab zu öffnen. Es kann nämlich serverseitig nicht erkannt werden, ob der Benutzer normal mit der linken Maustaste auf einen Link geklickt hat, um den Link im aktuellen Tab zu öffnen, oder ob er (z. B. über einen Klick auf die rechte Maustaste und das Kontextmenü) den Link in einem neuen Tab öffnet. Der Web-Request, der vom Browser abgesendet wird, ist in beiden Fällen identisch.

Hier hilft JavaScript weiter, und eine bereits fertige Lösung für das Problem liefert die Open-Source-Bibliothek Apache DeltaSpike, die seit Kurzem in der Version 1.0.1 verfügbar ist.

Zum einen macht DeltaSpike das Feature des Client-Windows auch für alle JSF-Versionen ab 2.0 verfügbar, zum anderen werden auch zwei weitere Client-Window-Modi eingeführt: LAZY und CLIENTWINDOW. Im Modus LAZY wird im Prinzip derselbe Mechanismus verwendet, den auch JSF ab Version 2.2 wie beschrieben verwendet, nur dass der Request-Parameter hier dswid heißt. Zusätzlich wird die dswid aber clientseitig (über JavaScript) im window.name gespeichert. Jene Variable ist pro Tab/Fenster eindeutig und wird beim Öffnen in einem neuen Tab auch nicht vererbt.

Lese mehr…


Keine Kommentare

Kontakt

OPEN KNOWLEDGE GmbH

Standort Oldenburg:
Poststraße 1, 26122 Oldenburg

Standort Essen:
II. Hagen 7, 45127 Essen