Viele webbasierte Enterprise-Anwendungen werden mit JavaServer Faces (JSF) erstellt. Diese Technologie ermöglicht es, aus einzelnen zustandslosen HTML-Seiten eine zustandsbehaftete Anwendung zu entwickeln, die aus einzelnen JSF-Komponenten besteht, welche für die Darstellung selbst verantwortlich sind. Bei jeder Browser-Anfrage wird der Zustand der Komponenten wiederhergestellt und nach der Verarbeitung wird der neue Zustand persistiert. Die Technologie sieht vor, dass bei der Entwicklung festgelegt wird, ob der Zustand auf dem Client oder auf dem Server gespeichert werden soll. Beide Varianten haben ihre Vor- und Nachteile, auf die ich hier nicht explizit eingehen möchte.
Ein Nachteil soll an dieser Stelle jedoch betrachtet werden: Wird der Zustand der Komponenten auf der Server-Seite (auch ’server-side state saving‘ genannt) vorgehalten, ist es nicht mehr möglich, mit mehreren Browser-Fenstern oder Tabs die Anwendungen zu benutzen – auch wenn die Logik in den Controllern dafür ausgelegt wurde.
Es wurde in solchen Fällen oft auf den „client-side state saving“-Mechanismus zurückgegriffen oder ein eigener StateManager implementiert. Bei dem „client-side state saving“ wird der Zustand der Komponenten (auch View genannt) serialisiert und an den Client „unsichtbar“ im HTML übertragen. Dies hat u.A. den Nachteil, dass Ajax-Requests relativ langsam sind, da der alte Zustand jedesmal übermittelt werden muss. Ein Wechsel von „client-side-“ auf „server-side state saving“ führt zwar zu einer besseren Performance, aber bei mehreren Browser-Fenstern und/oder -Tabs auch zu einer Exception:
javax.faces.application.ViewExpiredException: /myPage.xhtmlNo saved view state could be found for the view identifier: /myPage.xhtml at org.apache.myfaces.lifecycle.RestoreViewExecutor.execute(RestoreViewExecutor.java:132) at org.apache.myfaces.lifecycle.LifecycleImpl.executePhase(LifecycleImpl.java:170) at org.apache.myfaces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:117) at javax.faces.webapp.FacesServlet.service(FacesServlet.java:197) ....
Der Grund hierfür ist, dass pro HTTP-Session z.B. 20 Views auf dem Server gespeichert werden. Arbeitet man nun mit mehreren Tabs und vernachlässigt einen, so folgt die Exception beim Weiterarbeiten mit dem vernachlässigten Tab. Durch das Standard-Verhalten „First In – First Out“ haben die anderen Tabs alle Views mit neueren Versionen überschrieben und der Zustand von dem vernachlässigten Tab kann nicht mehr hergestellt werden.
Während es noch keine Standard-Lösung für das Problem im JSF-Standard gibt, hat das Apache Myfaces-Projekt ab der Version 2.0.6 und 2.1.0 eine eigene Lösung geschaffen. Man kann mit einem neuen Parameter die maximale Anzahl von zusammenhängenden Views begrenzen, so belegt jedes Fenster beispielsweise nur 5 der 20 Views, dies ermöglicht garantiert vier Tabs/Fenster pro HTTP-Session.
Dazu muss der neue Parameter wie folgt der web.xml hinzugefügt werden:
<context-param> <param-name>org.apache.myfaces.NUMBER_OF_SEQUENTIAL_VIEWS_IN_SESSION</param-name> <param-value>5</param-value> </context-param>
Das Ganze hat nur Einfluss auf den Komponenten-Baum, nicht aber auf die Controller bzw. das Modell im Backend. Dies muss natürlich in beiden Fällen für die Benutzung von mehreren Fenstern ausgelegt sein.