Die Scopes haben in den letzten Updates der Spezifikationen von JavaServer Faces (JSF) und Contexts and Dependency Injection (CDI) weitreichende Änderungen erfahren. Mit der JSF 1.0 Spezifikation standen lediglich vier Scopes zur Verfügung: None, Request, Session und Application. Besonders zwischen dem kurzen Request Scope, der an einen einzelnen http Request/Response Zyklus gebunden ist und dem sehr viel langlebigeren Session Scope tat sich eine Lücke auf, die oft zu einer Überladung des Session Scope mit eigentlich kurzlebigeren Daten führte. Durch die Einführung von CDI kam, neben den Pseudo-Scopes Singleton und Dependent, mit dem Conversation Scope ein möglicher Ansatz zur Schließung der zuvor beschriebenen Lücke hinzu. Mit der Conversation ist es möglich einen Scope mittels der Methoden einer Bean zu starten und zu beenden. Über diese Conversation lässt sich etwa eine mehrseitige Anmeldung oder Konfiguration, im Englischen meist als Wizard bezeichnet, realisieren ohne diese Daten nach Abschluss weiter in der Nutzersession zu halten. Der Conversation Scope hat jedoch den bedeutenden Nachteil, dass in einer Session immer nur eine Conversation zur selben Zeit genutzt werden kann. Eine Verschachtelung von Conversations ist somit nicht möglich.
In der unterstehenden Tabelle kann man eine Übersicht über die in JSF/CDI zur Verfügung stehenden Scopes bekommen.
Scope Name | Lebensdauer | Anwendungsbeispiel | Verfügbar ab Spezifikation |
Singleton | Programmlaufzeit | — | CDI 1.0 |
Dependent | Abhängig vom aufrufender Bean | — | CDI 1.0 |
None | Referenz | — | JSF 1.0 |
Request | http-Request | GET-Requests | JSF 1.0 |
Session | Dauer der Anwendersession | Nutzereinstellungen | JSF 1.0 |
Application | Programmlaufzeit | Konstanten, locale | JSF 1.0 |
Conversation | Methodenaufruf | Wizards | CDI 1.0 |
Flow | Definitionsabhängig | Wizards | JSF 2.2 |
View | Bildschirmanzeige | Dynamische Formulare | JSF 2.0/JSF 2.2 |
Transaction | JTA-Transaktion | Transaktion | CDI 1.1 |
Flash | Zwei http-Requests | Redirect | JSF 2.0 |
Custom | Frei wählbar | Frei wählbar | JSF 2.0 |
Nachfolgend sollen noch die neueren, in JSF 2.x eingeführten Scopes kurz vorgestellt werden.
Faces Flow ( @flowscoped)
Faces Flow wurde in JSF 2.2 eingeführt und lässt sich ähnlich wie der Conversation Scope für die Umsetzung von mehrseitigen Wizards etwa zur Konfiguration nutzen, ohne jedoch den Einschränkungen bezüglich Verschachtlung zu unterliegen. Des Weiteren zeigt sich bei der Erstellung eines Faces Flow ein Beispiel für das Prinzip „Convention over Configuration“. Die bevorzugte Variante einen Faces Flow zu erstellen ist es die im Flow enthaltenen Webseiten in einem Verzeichnis zu speichern, wobei das Verzeichnis nach dem Flow benannt sein muss und einen, ebenfalls nach dem Flow benannten, Einstiegspunkt für den Flow. Auf der nächsthöheren Verzeichnisebene kann eine Webseite als Ausstiegspunkt definiert werden, ebenfalls über die Benennung, mit Flowname-return. Neben dieser konventionsbasierten Methode lässt sich ein Flow noch auf zwei andere Arten definieren, über eine XML-Definitionsdatei und eine Producer-Bean. Die drei genannten Methoden zur Erstellung eines Flows werden unterhalb kurz skizziert.
Zunächst die Ordnerstruktur eines konventionsbasierten Flows:
/index.xhtml Einstieg in den Flow über Verweis auf die Starseite des Flows: <h:commandButton id=”start” value=”Enter Flow” action=”flowA”/>
/flowA-return.xhtml Durch Verweis auf diese Seite verlässt man den derzeitigen Flow
/flowA/flowA.xhtml Die Startseite von des Flows flowA, der Name der Seite muss identisch mit dem Namen des Verzeichnis und damit des Flows sein
/flowA/flowA-1.xhtml
/flowA/flowA-2.xhtml
/flowA/flowA-etc.xhtml Für die Bennenung der weiteren Seiten im Flow gibt es von Seiten der Flow Konvention keine Beschränkungen
/flowA/flowA-flow.xml An dieser Stelle kann eine XML-Definitionsdatei angelegt werden, deren Navigationsregeln die konventionsbasierten Regeln überschreiben können
Die Definition mittels einer XML-Datei und mittels einer Producer-Bean entsprechen sich semantisch, deshalb soll nur Letztere hier noch mit Code verdeutlicht werden, während für eine ausführlichere Betrachtung auf den entsprechenden Artikel im Oracle-Blog verwiesen wird.
//Die Bean produziert mit Hilfe der FlowBuilder API einen Flow auf den genau wie auf den konventionsbasierten Flow zugegriffen werden kann
@Produces
@FlowDefinition
public Flow defineFlow(@FlowBuilderParameter FlowBuilder builder){String flowID = “flow1”;
builder.id(“”, flowID);
builder.viewNode(“flow1”, “/flow1/flow1.xhtml”).markAsStartNode();builder.returnNode(“flow1-return”).fromOutcome(“/flow1-return”);
return builder.getFlow();
Neben dem Setzen von Start- und Zielseite lässt sich die Navigation innerhalb des Flow sowie die Kommunikation mit anderen Flows über Parameter noch weiter definieren, siehe dazu die FlowBuilder API Spezifikation.
View Scope (@viewscoped)
Der View Scope enthält Beans für eine bestimmte Webseite, unabhängig von der Anzahl der Requests während der Anzeige der Seite. Dies ist insbesondere bei dynamischen Webseiten mit Formularen und Tabellen nützlich, wo zahlreiche Requests in einer Anzeige stattfinden. Damit schließt der View Scope eine der größten Lücken zwischen dem Request und Session Scope. In der JSF Spezifikation 2.0 war der View Scope nur für JSF verfügbar, mit der JSF Spezifikation 2.2 lässt er sich jedoch auch für CDI Beans nutzen. Dadurch gibt es nun jedoch zwei Annotationen @viewscoped, die ältere ist unter javax.faces.bean.ViewScoped
zu finden, während die neuere unter javax.faces.view.ViewScoped
liegt.
Flash “Scope”
Obwohl im weiteren Sinne den Scopes zugehörig, unterscheidet sich der Flash in einem wichtigen Punkt von den anderen hier beschriebenen Scopes, es existiert keine dazugehörige Annotation um eine Bean dem Flash Scope zuzuordnen. Stattdessen handelt es sich beim Flash um ein Container-Objekt, in welchem ein Wert oder Objekt über ein Redirect hinaus erhalten werden kann ohne einen teureren Scope zu verwenden. Allerdings hatte der Flash in der Referenzimplementierung Mojarra mehrere gravierende Fehler, die seine Benutzung zunächst einschränkten, inzwischen aber behoben sind. Nachfolgend ein kurzes Code-Beispiel für die Anwendung des Flash:
In FlashTest.java: public String navigateTest() { FacesContext.getCurrentInstance().getExternalContext(). getFlash().put("param1", "Hello World!"); return "test.xhtml?faces-redirect=true"; } In test.xhtml: <h:outputLabel value="#{flash['param1']}" />
Custom Scopes (@customscoped)
Seit JSF 2.0 besteht die Möglichkeit einfacher als zuvor eigene Scopes für individuelle Problemstellungen zu entwickeln. Um einen mit @customscoped kompatiblen Scope zu erstellen, werden drei Komponenten benötigt: Ein Container zur Verwahrung der im Scope befindlichen Objekte, eine Klasse zum Auflösen der Expression Language, wodurch ein Zugriff aus JSF auf dem Scope möglich wird und eine Möglichkeit, den Lebenszyklus des Scopes zu steuern. Der erwähnte Container erbt von der Collection ConcurrentHashMap, um Nebenläufigkeit zu garantieren, etwa falls der Nutzer in mehreren Fenstern der Anwendung gleichzeitig arbeitet. Die Klasse zum Auflösen des Aufrufs in der Expression Language erbt von der Klasse ELResolver, die die Standardauflösung der Expression Language übernimmt. Der Lebenszyklus des Scopes kann auf verschiedene Weise geregelt werden, etwa durch eine gesonderte Bean oder durch Methoden in den anderen beiden benötigten Klassen. Zur Verdeutlichung des Geschriebenen wird auf dieses ausführliche Beispiel verwiesen.