View-Config mit Page-Beans
 
  View-Config mit CODI
 
  View-Config mit CODI
 
  View-Config im Einsatz
 
  Service-Beans mit und ohne Qualifier
 
  Qualifier in CDI
 
  Producer-Methode mit Parameter
 
  Producer-Methode für Konverter
 
  Producer-Methode für eine Zufallszahl
 
  Page-Bean
 
  MyGourmet 17: View-Config
 
  MyGourmet 17: Page-Bean des Wizards zum Anlegen eines Kunden
 
  MyGourmet 17: Page-Bean der Kundenansichten
 
  MyGourmet 17: Page-Bean der Kunden-Übersichtsseite
 
  Minimale beans.xml
 
  Manuelles Beenden einer Konversation in CODI
 
  Listener für OpenWebBeans in web.xml
 
  Konversationsgruppen in CODI
 
  Dependency-Injection mit @Inject und Qualifier
 
  Dependency-Injection mit @Inject
 
  CODI Window-Context
 
  CDI-Managed-Bean im View-Scope
 
  CDI-Bean im Window-Scope von CODI
 
  CDI-Bean im View-Access-Scope von CODI
 
  CDI-Bean im Conversation-Scope von CODI
 
  CDI-Bean
 
  Bean-Definition und Dependency-Injection mit CDI
 
  @Inject mit Konversationsgruppen

9 JSF und CDI

Mit Contexts and Dependency Injection for Java (CDI) enthält Java EE ab Version 6 einen neuen Standard, um Beans zu verwalten und über Dependency-Injection miteinander zu verbinden. Der Einsatz von CDI in JSF-Projekten bietet eine ganze Reihe von Vorteilen gegenüber dem Einsatz der internen Managed Bean Creation Facility . CDI bringt unter anderem einen viel mächtigeren, typsicheren Dependency-Injection-Mechanismus, Unterstützung für Interceptors und Decorators und eine sehr einfache Interaktion über Ereignisse mit, um nur einige der Vorteile zu nennen - und das alles, ohne die Komplexität der Anwendung merklich zu erhöhen. Aus Sicht von JSF macht es keinen Unterschied, ob Beans über CDI oder JSF-intern verwaltet werden. Die Verbindung zwischen der Ansicht und dem Modell erfolgt in beiden Fällen über die Unified-EL .
CDI unterstützt den Standard Dependency Injection for Java (JSR-330, auch At Inject genannt). JSR-330 standardisiert erstmals die wichtigsten Annotationen für die Dependency-Injection und ermöglicht den Einsatz derselben Annotationen aus dem Package javax.inject in unterschiedlichen Umgebungen wie Java EE 6 , Spring 3 oder Guice 2 .
Nach einer Einführung in die Konzepte von CDI in Abschnitt [Sektion:  Beans und Dependency-Injection mit CDI] finden Sie in Abschnitt [Sektion:  Konfiguration von CDI] Hinweise zur Konfiguration. Im Anschluss daran zeigt Abschnitt [Sektion:  MyGourmet 16: Integration von CDI] Details zum Beispiel MyGourmet 16 . Abschnitt [Sektion:  Konversationen mit JSF] widmet sich der Abbildung von Geschäftsprozessen mit Konversationen. Abschließend zeigen wir Ihnen in Abschnitt [Sektion:  Apache MyFaces CODI] noch, wie Sie Konversationen mit CDI und dem Projekt Apache MyFaces CODI einsetzen.

9.1 Beans und Dependency-Injection mit CDI

Die Hauptaufgabe von CDI besteht in der Verwaltung von Beans mit einem definierten Lebenszyklus. CDI stellt zu diesem Zweck Mechanismen zur Verfügung, um Beans zu definieren und über typsichere Dependency-Injection miteinander zu verknüpfen.
CDI unterstützt unterschiedliche Arten von Beans:
Wir zeigen Ihnen, wie Sie Beans auf Basis von Klassen und mit Producer-Methoden definieren.

9.1.1 Managed-Beans mit CDI

Der Weg zur ersten CDI-Bean ist ausgesprochen einfach: CDI kennt keine spezielle Annotation, um Beans auf Basis von Klassen zu definieren, und macht aus so gut wie allen Klassen Beans. Ausgenommen sind zum Beispiel nicht statische, innere Klassen oder Klassen, die über keinen geeigneten Konstruktur verfügen. Ein für CDI geeigneter Konstruktor hat entweder keine Parameter oder ist mit @Inject annotiert.
Jede CDI-Bean hat einen Gültigkeitsbereich (Scope), der mit einer Annotation auf der Bean-Klasse definiert wird. CDI unterstützt standardmäßig folgende Gültigkeitsbereiche und definiert dafür Annotationen im Package javax.enterprise.context :
Tipp: CDI-Beans in sogenannten Passivating-Scopes wie @SessionScoped oder @ConversationScoped müssen serialisierbar sein.
Listing CDI-Bean zeigt die Definition einer CDI-Bean mit der Klasse CustomerService im Application-Scope. Achten Sie bitte speziell auf das Package der Annotation @ApplicationScoped . Es handelt sich hier um die von CDI definierte Annotation und nicht um das JSF-Pendant mit dem gleichen Namen.
import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class CustomerService {
  ...
}
In CDI ist jetzt eine Bean vom Typ CustomerService im Application-Scope verfügbar, die in jede andere Bean injiziert werden kann. Das Annotieren eines Feldes mit @Inject reicht aus, um eine Abhängigkeit auf eine andere Bean zu definieren. Listing Dependency-Injection mit @Inject zeigt die Bean CustomerBean mit einer Abhängigkeit auf die zuvor definierte Bean vom Typ CustomerService .
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;

@Named
@RequestScoped
public class CustomerBean {
  @Inject
  private CustomerService customerService;
  ...
}
Beim Erstellen einer Bean-Instanz vom Typ CustomerBean löst CDI diese Abhängigkeit auf und injiziert die Bean-Instanz für den Typ CustomerService aus dem Application-Scope.
Mit der Annotation @Named erhält die Bean zusätzlich einen Namen, unter dem sie in Unified-EL-Ausdrücken verfügbar ist. Auch hier gilt wie bei Managed-Beans in JSF: Wenn im Element value von @Named kein expliziter Name angegeben wird, erzeugt CDI per Konvention den Namen aus dem Klassennamen mit einem kleinen Anfangsbuchstaben. Aus dem Klassennamen CustomerBean wird zum Beispiel der Name cust-omer-Bean , der dann wie folgt in einer Ansicht verwendet werden kann:
<h:inputText value="#{customerBean.property}"/>
Für den Fall, dass es mehrere Beans gleichen Typs gibt, muss die Auswahl weiter eingeschränkt werden. Dazu sieht CDI sogenannte Qualifier vor. Ein Qualifier ist eine beliebige Annotation, die mit @javax.inject.Qualifier annotiert ist. Listing Qualifier in CDI zeigt als Beispiel den Qualifier @Special .
@Retention(RUNTIME)
@Target({TYPE, FIELD, METHOD})
@Qualifier
public @interface Special {
}
Mit einem solchen Qualifier wird dann einerseits die Klasse der Bean und andererseits der Injektionspunkt annotiert. Aber sehen wir uns das am besten anhand eines Beispiels an. Listing Service-Beans mit und ohne Qualifier zeigt zwei unterschiedliche Implementierungen des Interface Service . Da beide Beans den Typ Service haben (wir gehen davon aus, dass nur das Interface nach außen hin bekannt ist), annotieren wir zur weiteren Unterscheidung die Klasse SpecialServiceImpl mit unserem Qualifier @Special .
@ApplicationScoped
public class ServiceImpl implements Service {
  ...
}

@ApplicationScoped
@Special
public class SpecialServiceImpl implements Service {
  ...
}
Dieselbe Qualifier-Annotation wird auch beim Injektionspunkt benutzt, um die Auswahl der Beans vom Typ Service auf das gewünschte Exemplar einzuschränken. Listing Dependency-Injection mit @Inject und Qualifier zeigt das entsprechende Codefragment.
public class MyBean {
  @Inject @Special
  private Service service;
}
Die Annotation @Named ist übrigens auch ein Qualifier, der zum Einschränken der Auswahl anhand des Namens der Bean eingesetzt werden kann. Wir raten allerdings davon ab, das zu tun, da damit der Vorteil der Typsicherheit verloren geht.
CDI-View-Scope, JSF 2.2: Falls Sie den View-Scope in CDI vermissen, haben wir eine schlechte und eine gute Nachricht für Sie. Die schlechte Nachricht ist, dass CDI standardmäßig keinen View-Scope kennt. Die gute Nachricht ist, dass JSF 2.2 hier Abhilfe schafft und den View-Scope für CDI nachreicht. Listing CDI-Managed-Bean im View-Scope zeigt eine von CDI verwaltete Managed-Bean im neuen CDI-View-Scope von JSF 2.2. Verwechseln Sie die Annotation javax.faces.view.ViewScoped für den CDI-View-Scope nicht mit der Annotation javax.faces.bean.ViewScoped für den JSF-View-Scope.
@javax.inject.Named
@javax.faces.view.ViewScoped
public class CustomerBean {
  @Inject
  private CustomerService customerService;
  ...
}
Apache MyFaces CODI bietet mit dem View-Access-Scope eine Alternative zum klassischen View-Scope - auch ohne JSF 2.2. Details dazu finden Sie in Abschnitt [Sektion:  Apache MyFaces CODI] und im Beispiel MyGourmet 17 .

9.1.2 Producer-Methoden

Die zweite hier vorgestellte Variante zur Definition von Beans mit CDI sind die sogenannten Producer-Methoden. Wie der Name bereits erahnen lässt, werden Beans mit diesem Konzept über spezielle Methoden definiert. Alle nicht abstrakten Methoden einer Managed-Bean oder Session-Bean, sowohl statische wie auch nicht statische, können als Producer-Methoden fungieren. Damit eine Methode zur Producer-Methode wird, muss sie mit @Produces annotiert werden. Der Rückgabetyp der Methode definiert dabei den Typ der Bean und der Rückgabewert wird als Bean-Instanz verwendet. Producer-Methoden können wie Bean-Klassen mit Scope- und Qualifier-Annotationen versehen werden. Listing Producer-Methode für eine Zufallszahl zeigt ein Beispiel, in dem eine Zufallszahl vom Typ Integer mit dem Qualifier @Random als Bean zur Verfügung gestellt wird.
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
import javax.inject.Named;
import at.irian.Random;

@ApplicationScoped
public class RandomProducer {
  private java.util.Random random = new java.util.Random();
  @Produces @Named @Random
  public int getRandom() {
    return random.nextInt(1000);
  }
}
Der entscheidende Vorteil von Producer-Methoden besteht darin, dass die Erstellung der Bean-Instanz komplett im Verantwortungsbereich der Applikation liegt. Damit lassen sich auch Beans für Klassen definieren, die CDI nicht direkt verwenden kann (wie etwa Klassen aus dem JDK) oder die eine spezielle Initialisierung benötigen.
Die im Beispiel von der Producer-Methode erzeugte Zufallszahl kann in einer anderen Bean verwendet werden, indem ein Feld vom Typ int mit @Inject und dem Qualifier @Random annotiert wird:
public class MyBean {
  @Inject @Random
  private int random;
}
Nachdem der Scope nicht explizit definiert ist, kommt für die Zufallszahl der Dependent-Scope zum Einsatz. Der Scope der Bean RandomProducer und der Scope der erzeugten Bean haben keine direkte Beziehung zueinander. Verwendet eine Producer-Methode aber zum Beispiel Daten aus der Bean, kann deren Scope durchaus eine Rolle spielen.
Da die Producer-Methode zusätzlich mit @Named annotiert ist, steht die Zufallszahl auch direkt in Unified-EL-Ausdrücken zur Verfügung:
<h:outputText value="#{random}"/>
Der Name der Bean leitet sich per Konvention aus dem Methodennamen ab, falls er nicht in @Named angegeben ist. Bei Getter-Methoden nach dem JavaBeans -Standard wird der Name der Eigenschaft verwendet. Im Beispiel wird daher aus getRandom() der Name random .
Producer-Methoden können auch Parameter enthalten. Listing Producer-Methode mit Parameter zeigt eine Producer-Methode mit einem Parameter vom Typ UserBean . Beim Aufruf der Methode löst CDI die aktuelle Bean-Instanz für diesen Typ auf und übergibt sie an die Methode. Dort wird aus der übergebenen Bean der Name des aktuell eingeloggten Benutzers ausgelesen und als eigene Bean mit dem Typ String und dem Qualifier @UserName im Dependent-Scope zur Verfügung gestellt.
@ApplicationScoped
public class UsernameProducer {
  @Produces @Named @UserName
  public String getUserName(UserBean userBean) {
    return userBean.getUserName();
  }
}
Listing Producer-Methode für Konverter zeigt eine interessante Einsatzmöglichkeit für Producer-Methoden in JSF-Applikationen. Die gezeigte Klasse ConverterProducer stellt über die Methode getCustomConverter() den Konverter mit der Klasse CustomConverter als Bean zur Verfügung.
@ApplicationScoped
public class ConverterProducer {
  @Produces @Named
  public CustomConverter getCustomConverter() {
    return new CustomConverter();
  }
}
Der Einsatz des Konverters sieht wie im folgenden Beispiel aus:
<h:inputText value="#{bean.property}"
    converter="#{customConverter}"/>
In Abschnitt Sektion:  Definition mit Java zeigen wir Ihnen, wie Sie mit JSF 2.2 Faces-Flows über CDI-Producer-Methoden definieren können.

9.2 Konfiguration von CDI

CDI ist wie JSF nur eine Spezifikation, für die es mehrere Implementierungen gibt. Zu den bekanntesten zählen zurzeit Weld von JBoss (die Referenzimplementierung) und Apache OpenWebBeans . Grundsätzlich gibt es zwei Varianten, um CDI einzusetzen. Wenn die Anwendung auf einem Applikationsserver läuft, der Java EE 6 unterstützt (wie Glassfish 3 oder JBoss AS 7 ), ist CDI bereits integriert und einsatzbereit. Läuft die Applikation hingegen nur auf einem Servlet-Container wie Tomcat oder Jetty , muss eine CDI-Implementierung manuell integriert werden.
Nachdem unsere MyGourmet -Beispiele auf Jetty laufen, haben wir uns für die zweite Variante mit OpenWebBeans entschieden, da sich die Integration in eine JSF-Anwendung sehr einfach gestaltet. Nach dem Einbinden aller benötigten Jar-Dateien muss nur noch der Listener WebBeansConfigurationListener wie in Listing Listener für OpenWebBeans in web.xml in der web.xml eingetragen werden. Die Liste aller benötigten Abhängigkeiten finden Sie im Quellcode zu Beispiel MyGourmet 16 in der pom.xml .
<listener>
  <listener-class>
    org.apache.webbeans.servlet.WebBeansConfigurationListener
  </listener-class>
</listener>
Damit CDI Beans in einer Webapplikation findet, muss die Datei im Verzeichnis WEB-INF existieren. Beans in Jar-Dateien findet CDI hingegen nur, wenn die Datei beans.xml im Verzeichnis META-INF existiert. Die Datei beans.xml kann Konfigurationen für CDI enthalten, bleibt aber im einfachsten Fall leer, wie Listing Minimale beans.xml zeigt.
<beans xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
        http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>
Tipp: CDI funktioniert nur, wenn die Datei beans.xml im Verzeichnis WEB-INF oder META-INF existiert.

9.3 MyGourmet 16: Integration von CDI

In MyGourmet 16 dreht sich alles um die Integration von CDI. Dazu wird OpenWebBeans über Abhängigkeiten in der pom.xml in das Maven-Projekt eingebunden (Details entnehmen Sie bitte direkt dem Sourcecode). Die zusätzlich notwendige Konfiguration in der web.xml entspricht genau der im letzten Abschnitt vorgestellten.
Die Umstellung in MyGourmet beschränkt sich auf das Austauschen der JSF-Annotationen durch ihre JSR-330- und CDI-Pendants in den Klassen ProviderServiceImpl , AddressBean , CustomerBean , ProviderBean und ProviderListBean . Die Bean ProviderBean wurde mit der Annotation javax.faces.view.ViewScoped auf den CDI-View-Scope von JSF 2.2 umgestellt.
Listing Bean-Definition und Dependency-Injection mit CDI zeigt die Konfiguration der Beans mit den Klassen ProviderService und ProviderBean . Die JSR-330-Annotation @Inject auf dem Feld providerService der Klasse ProviderBean bewirkt, dass beim Erstellen einer Bean mit dem Typ ProviderBean die Bean mit dem Typ ProviderService in das Feld injiziert wird.
@ApplicationScoped
public class ProviderServiceImpl implements ProviderService {
  ...
}

@Named @SessionScoped
public class ProviderBean implements Serializable {
  @Inject
  private ProviderService providerService;
    ...
}
Was wir Ihnen hier gezeigt haben, ist die einfachste Form der Bean-Definition mit CDI und JSR-330. Sobald CDI läuft und in JSF eingebunden ist, steht Ihnen die komplette Palette der Möglichkeiten offen - und glauben Sie uns, das sind eine Menge. Ein wichtiges Einsatzgebiet für die Verwendung von CDI mit JSF sind Konversationen. In Abschnitt [Sektion:  Konversationen mit JSF] finden Sie allgemeine Informationen zu diesem Thema und Abschnitt [Sektion:  Apache MyFaces CODI] zeigt, wie Sie Konversationen mit CDI und Apache MyFaces CODI einsetzen.

9.4 Konversationen mit JSF

In vielen Webanwendungen lassen sich die zugrunde liegenden Geschäftsprozesse nicht direkt auf den Seitenfluss abbilden. Viele Prozesse, die aus Sicht des Benutzers eine abgeschlossene Einheit bilden, erstrecken sich in der Anwendung über mehrere Anfragen oder sogar über mehrere Ansichten hinweg. Denken Sie zum Beispiel an die Registrierung eines Benutzers, bei der im ersten Schritt die Login-Daten und im zweiten Schritt Daten zur Person abgefragt werden. Für den Benutzer der Anwendung ist dieser Vorgang eine in sich abgeschlossene Tätigkeit, die mit dem Anzeigen der ersten Ansicht beginnt und durch das Betätigen der Fertigstellen-Schaltfläche im zweiten Schritt abgeschlossen wird. Aus Sicht der Webanwendung handelt es sich dabei allerdings nur um eine Reihe von Anfragen auf zwei verschiedene Seiten.
Dadurch stellt sich die Frage, in welchem Gültigkeitsbereich die Daten während des Prozesses abgelegt werden müssen, damit sie in jedem Schritt zur Verfügung stehen. Managed-Beans im Request-Scope werden nach jeder Anfrage neu erzeugt und eignen sich daher nicht. Der View-Scope ist auch nur dann ausreichend, wenn der Prozess nicht mehr als eine Ansicht umfasst. Managed-Beans im Application-Scope eignen sich nicht für unsere Zwecke, da sie nur einmal pro Anwendung erzeugt werden und dadurch alle Benutzer dieselben Daten sehen. Bleibt als letzte Alternative nur noch der Session-Scope übrig. Der Session-Scope löst zwar das Verfügbarkeitsproblem während des Prozesses, bringt aber einige entscheidende Nachteile mit sich.
An dieser Stelle kommen Konversationen ins Spiel. Konversationen sind der ideale Speicherort für Managed-Beans, deren Lebensdauer über eine Anfrage oder Ansicht hinausgeht. Bei Webanwendungen tritt dieser Fall öfters ein, da sich Geschäftsprozesse nicht immer direkt auf den Seitenfluss der Applikation abbilden lassen. Konversationen bieten einige entscheidende Vorteile gegenüber der Session:
Wenn Sie CDI einsetzen, kann JSF sehr einfach um Konversationen erweitert werden. Das Projekt Apache MyFaces CODI bietet neben Konversationen noch eine Vielzahl von Erweiterungen zur nahtlosen Integration von JSF und CDI. Details zu CODI finden Sie in Abschnitt [Sektion:  Apache MyFaces CODI] .

9.5 Apache MyFaces CODI

Das Projekt Apache MyFaces Extensions CDI (kurz CODI) ist eine portable Erweiterung von CDI, die unter dem Dach von Apache MyFaces entwickelt wird. CODI bietet eine ganze Reihe von Features, um die Integration von JSF und CDI so einfach wie möglich zu gestalten.
CODI besteht aus mehreren Modulen, die sich je nach Bedarf in die Anwendung einbinden lassen. Neben einem Core-Modul für die Basisfunktionalität gibt es unter anderem noch Module für JSF, JPA, Messaging und Bean-Validation. CODI zeigt sich sehr flexibel, was seine Umgebung betrifft. Es läuft CDI-seitig mit Apache OpenWebBeans und JBoss Weld und JSF-seitig mit Apache MyFaces und Mojarra .
CDI kann an und für sich als äußerst gelungen bezeichnet werden, das Konversationskonzept ist allerdings eher dürftig. Als eines der wichtigsten Features bietet CODI daher im JSF-Modul eine erweiterte Unterstützung von Konversationen für CDI. In Abschnitt [Sektion:  Konversationen mit CODI] werfen wir daher einen etwas genaueren Blick auf dieses Thema.
Ein weiteres interessantes Feature von CODI ist die typsichere Konfiguration von Ansichten und die damit mögliche Definition von Page-Beans. Dabei handelt es sich um Beans, die an eine Ansicht gebunden sind und bei der Ausführung des Lebenszyklus an mehreren Stellen, wie etwa kurz vor dem Rendern der Ansicht, benachrichtigt werden. Details dazu finden Sie in Abschnitt [Sektion:  View-Config und Page-Beans] .
Abschließend zeigt Abschnitt [Sektion:  MyGourmet 17: Apache MyFaces CODI] das Beispiel MyGourmet 17 , in dem einige CODI-Features in die Praxis umgesetzt werden.

9.5.1 Konversationen mit CODI

CODI bietet ein sehr flexibles Konversationskonzept und ermöglicht sogar die Verwendung mehrerer Konversationen auf einer Seite. Eine Konversation ist dabei immer an das aktuelle Browserfenster beziehungsweise an den Browsertab gebunden. Es gibt somit keine Probleme, wenn die Anwendung in mehreren Fenstern oder Tabs läuft.
Eine Konversation ist genau wie die Session oder die HTTP-Anfrage als Gültigkeitsbereich für Managed-Beans einsetzbar. Anders als in CDI beginnt in CODI die Lebensdauer einer Konversation mit dem ersten Zugriff auf eine Bean in der Konversation. Die Lebensdauer hängt in CODI vom Typ der Konversation ab und kann unterschiedlich lange ausfallen. Eine Konversation kann allerdings nie länger als die Session dauern, da sie in der Session abgelegt ist.
Das JSF-Modul von CODI bietet eine ganze Reihe unterschiedlicher Konversationen und bringt auch gleich die passenden Annotationen mit, um sie als Scopes für CDI-Beans zu verwenden:

9.5.1.1 Conversation-Scope

Der Conversation-Scope von CODI definiert eine Konversation mit manueller Lebensdauer für CDI-Beans. Listing CDI-Bean im Conversation-Scope von CODI zeigt die Bean-Klasse WizardBean mit den notwendigen Annotationen. Diese Klasse könnte zum Beispiel als Bean für einen mehrstufigen Wizard mit den Seiten step1.xhtml , step2.xhtml und step3.xhtml verwendet werden.
@Named
@ConversationScoped
public class WizardBean implements Serializable {
  ...
}
Tipp: Verwechseln Sie die CODI-Annotation @ConversationScoped nicht mit der gleichnamigen CDI-Annotation. Sie unterscheiden sich lediglich durch das Package.
Wie verhält sich diese Bean in der Praxis, wenn ein Benutzer zum Beispiel die Seite step1.xhtml aufruft? Die Bean und die Konversation werden beim ersten Zugriff auf die Bean erstellt. Da es sich um eine manuelle Konversation handelt, steht sie auch für die Seiten step2.xhtml und step3.xhtml zur Verfügung, bis sie manuell oder durch einen Timeout beendet wird.
Das manuelle Beenden der Konversation wird über einen Aufruf der Methode close() auf der Instanz der aktuellen Konversation erledigt. Die aktuelle Konversation lassen wir uns dabei direkt von CDI in die Bean injizieren. Listing Manuelles Beenden einer Konversation in CODI zeigt nochmals die Klasse WizardBean mit den Methoden save() und cancel() . In beiden Methoden wird die Konversation beendet.
@Named @ConversationScoped
public class WizardBean implements Serializable {
  @Inject
  private Conversation conversation;
  public String save() {
    conversation.close();
    return "details.xhtml";
  }
  public String cancel() {
    conversation.close();
    return "overview.xhtml";
  }
}
Standardmäßig erstellt CODI für jede Bean im Conversation-Scope eine eigene Konversation. Dadurch ist es auch möglich, mehrere Konversationen auf einer Seite zu haben. Manchmal ist es aber erwünscht, mehrere Beans in einer Konversation zusammenzufassen. Dazu gibt es in CODI das Konzept der sogenannten Konversationsgruppen. Mit der Annotation @ConversationGroup kann eine beliebige Java-Klasse als typsichere ID der Konversationsgruppe definiert werden. Listing Konversationsgruppen in CODI zeigt die Beans WizardStep1 und WizardStep2 , die beide in derselben, durch das Interface Wizard identifizierten Konversation liegen.
public interface Wizard {}

@ConversationScoped
@ConversationGroup(Wizard.class)
public class WizardStep1 implements Serializable {
  ...
}

@ConversationScoped
@ConversationGroup(Wizard.class)
public class WizardStep2 implements Serializable {
  ...
}
Da sich beide Beans eine Konversation teilen, werden beim Schließen der Konversation auch beide Beans aus dem Speicher entfernt. Technisch gesehen ist die Annotation @ConversationGroup ein Qualifier und muss daher auch verwendet werden, wenn eine Abhängigkeit auf eine damit annotierte Bean erstellt wird. Listing @Inject mit Konversationsgruppen zeigt ein Beispiel.
public class Wizard {
  @Inject @ConversationGroup(Wizard.class)
  private WizardStep1 step1;
  @Inject @ConversationGroup(Wizard.class)
  private WizardStep2 step2;
}
Wird keine explizite Konversationsgruppe angegeben, verwendet CODI intern die Klasse der Bean als ID der Konversation.

9.5.1.2 View-Access-Scope

Der View-Access-Scope von CODI definiert eine Konversation mit automatischer Lebensdauer für CDI-Beans. Grundsätzlich erstreckt sich die Lebensdauer einer Bean im View-Access-Scope immer auf die Ansicht, in der sie momentan verwendet wird, und auf die nächste Ansicht. Listing CDI-Bean im View-Access-Scope von CODI zeigt die Bean DetailsBean im View-Access-Scope.
@Named
@ViewAccessScoped
public class DetailsBean implements Serializable {
  ...
}
Sehen wir uns dazu ein Beispiel an. Findet der erste Zugriff auf DetailsBean in der Seite showDetails.xhtml statt, werden die Konversation und die Bean erstellt. Die Lebensdauer der Konversation erstreckt sich jetzt per Definition auf die Seite showDetails.xhtml und auf die nächste Seite. Solange der Benutzer auf dieser Seite bleibt - etwa weil Ajax-Anfragen ausgeführt werden -, ist die Konversation aktiv. Navigiert der Benutzer im nächsten Schritt auf die Seite editDetails.xhtml , bleibt die Konversation weiter aktiv. Der entscheidende Punkt ist jetzt, ob auf dieser Seite ein Zugriff auf die Bean erfolgt. Falls ja, verlängert CODI die Laufzeit um eine weitere Ansicht. Falls nein, wird die Konversation aus dem Speicher entfernt, sobald der Benutzer auf eine Seite mit unterschiedlicher View-ID navigiert.
Konversationen mit automatischer Lebensdauer sind zwar sehr praktisch, können aber zu unerwarteten Ergebnissen führen, wenn eine Bean - vielleicht unbeabsichtigt - auf mehreren Seiten referenziert wird.

9.5.1.3 Window-Scope

Der Window-Scope von CODI definiert eine Konversation mit manueller Lebensdauer für CDI-Beans. Die Konversation ist dabei an ein Browserfenster beziehungsweise an einen Browsertab gebunden und fungiert als eine Art Session pro Fenster beziehungsweise Tab. Listing CDI-Bean im Window-Scope von CODI zeigt eine Bean im Window-Scope.
@Named
@WindowScoped
public class SettingsBean implements Serializable {
  ...
}

9.5.1.4 Window-Context

Wenn Sie MyGourmet 17 starten und im Browser durch die Anwendung klicken, werden Sie bemerken, dass jede URL der Anwendung den Request-Parameter mit dem Namen windowId aufweist. CODI benutzt den Wert dieses Parameters, um Fenster und Tabs derselben Browserinstanz zu unterscheiden. Damit sich Konversationen eindeutig auf ein Fenster oder einen Tab des Browsers abbilden lassen, führt CODI den Window-Context ein. Der Wert des Parameters windowId ist die ID des aktuellen Window-Contexts, in dem alle Konversationen eines Fensters oder Tabs in der Session abgelegt sind.
Den Window-Context kann man sich wie die Konversation direkt von CDI einimpfen lassen. Ein Schließen des Window-Contexts beendet alle darin befindlichen Konversationen. Listing CODI Window-Context zeigt ein Beispiel.
@WindowScoped
public class WindowBean implements Serializable {

  @Inject
  private WindowContext windowContext;

  public void closeWindow() {
    windowContext.close();
  }
}

9.5.2 View-Config und Page-Beans

Ein weiteres interessantes Feature von CODI ist die typsichere Konfiguration von Ansichten mit View-Config-Klassen. Dank impliziter Navigation erlaubt ja JSF ab Version 2.0 an vielen Stellen die direkte Verwendung der View-ID zur Navigation. Das ist zwar sehr praktisch, kann aber mit steigender Größe der Applikation zu Problemen führen, wenn XHTML-Dateien umbenannt oder umstrukturiert werden.

9.5.2.1 View-Config

Die grundlegende Idee einer View-Config ist, Seiten nicht mehr über ihre View-IDs, sondern über spezielle Klassen zu referenzieren. Eine View-Config-Klasse wird einmal zentral definiert und dann im gesamten Projekt stellvertretend für eine Seite verwendet. Intern bildet CODI die View-Config wieder auf eine View-ID ab.
Listing View-Config mit CODI zeigt eine erste View-Config. Die Klasse muss nur das Interface ViewConfig implementieren und mit @Page annotiert werden.
@Page
public class Overview implements ViewConfig {}
Die Verbindung zwischen der Klasse und der View-ID erfolgt per Konvention über den Klassennamen. Aus der Klasse Overview leitet CODI zum Beispiel die View-ID /overview.xhtml ab.
Listing View-Config im Einsatz zeigt, wie die View-Config aus Listing View-Config mit CODI in einer Action-Methode zur Navigation eingesetzt wird. Statt der View-ID wird jetzt einfach die View-Config-Klasse zurückgeliefert. Dazu muss natürlich der Rückgabewert der Methode angepasst werden, was mit JSF ab Version 2.0 aber kein Problem darstellt.
@ViewAccessScoped
public class DetailsBean implements Serializable {
  public Class<? extends ViewConfig> save() {
    return Overview.class;
  }
}
Im vorherigen Beispiel sind wir davon ausgegangen, dass alle Seitendeklarationen im Wurzelverzeichnis der Applikation liegen. CODI ermöglicht mit der typsicheren Konfiguration aber auch das Abbilden von Verzeichnisstrukturen in Form von Klassenhierarchien. Sehen wir uns dazu als Beispiel in Listing View-Config mit CODI die Konfiguration für die Seiten details.xhtml und overview.xhtml im Verzeichnis pages an.
public interface Pages extends ViewConfig {
  @Page
  public final class Overview implements Pages {}
  @Page
  public final class Details implements Pages {}
}
Das Interface Pages repräsentiert das Verzeichnis pages und ist vom Interface ViewConfig abgeleitet. Die konkreten View-Config-Klassen Overview und Details sind als innere Klassen umgesetzt, die jetzt nicht mehr direkt ViewConfig , sondern Pages implementieren. Aus der Klasse macht CODI intern die View-ID /pages/details.xhtml - genau was wir erreichen wollten.
Als angenehmer Nebeneffekt sind die View-Config-Klassen dadurch übersichtlich in einem Interface zusammengefasst. Eine solche Gruppierung ist übrigens auch dann möglich, wenn die Seiten nicht in einem Verzeichnis liegen. Dazu muss das Interface lediglich mit der Annotation @Page(basePath="") versehen werden, wie Listing View-Config mit Page-Beans zeigt. Aus der Klasse Details wird wieder die View-ID /details.xhtml .

9.5.2.2 Page-Beans

Mit den View-Config-Klassen bietet CODI die Möglichkeit, Page-Beans für Ansichten zu definieren. Dabei handelt es sich um eine Bean, die an eine Ansicht gebunden ist und bei der Ausführung des Lebenszyklus an mehreren Stellen benachrichtigt wird. Die Verbindung zwischen Ansicht und Page-Bean wird mit der Annotation @PageBean auf der View-Config-Klasse definiert. Listing View-Config mit Page-Beans zeigt ein Beispiel.
@Page(basePath = "")
public interface Pages extends ViewConfig {
  @Page
  @PageBean(OverviewBean.class)
  public final class Overview implements Pages {}
  @Page
  @PageBean(DetailsBean.class)
  public final class Details implements Pages {}
}
Sobald eine Ansicht mit einer Page-Bean verbunden ist, wird die Bean von CODI zu bestimmten Zeitpunkten während des Lebenszyklus benachrichtigt. Dabei werden folgende Methoden aufgerufen:
Listing Page-Bean zeigt ein Beispiel für eine Page-Bean mit zwei annotierten Methoden.
@ViewAccessScoped
public class OverviewBean {
  @InitView
  public void init() {...}
  @PreRenderView
  public void loadData() {...}
}

9.5.3 MyGourmet 17: Apache MyFaces CODI

MyGourmet 17 integriert Apache MyFaces CODI und zeigt einige Anwendungsfälle. Das Beispiel verwendet das Core- und das JSF-2.0-Modul von CODI. Beide Module werden über Abhängigkeiten in der pom.xml in das Maven-Projekt eingebunden. Details entnehmen Sie bitte direkt dem Sourcecode. Auf die Konfiguration von CDI werden wir hier nicht mehr näher eingehen.
In MyGourmet 17 haben wir den Kundenbereich der Anwendung leicht umgebaut. Die Startseite ist jetzt customerList.xhtml , auf der eine Liste aller Kunden mit mc:dataTable dargestellt wird. Von dieser Ansicht aus kann der Benutzer zur Detailseite eines Kunden navigieren, einen neuen Kunden anlegen oder einen vorhandenen Kunden löschen.
Die Bean CustomerListBean im View-Access-Scope ist als Page-Bean der Ansicht definiert. Listing MyGourmet 17: Page-Bean der Kunden-Übersichtsseite zeigt die Klasse. Die Liste der Kunden wird in der Methode preRenderView geladen. Da sie mit @PreRenderView annotiert ist, wird sie vor jedem Rendern der Ansicht von CODI aufgerufen. Zum Löschen eines Kunden wird über eine h:commandLink -Komponente die Methode deleteCustomer aufgerufen. Der zu löschende Kunde wird dabei direkt als Parameter übergeben:
<h:commandLink value="#{msgs.delete}"
    action="#{customerListBean.deleteCustomer(customer)}">
  <f:ajax render=":form:addressPanel:addresses"/>
</h:commandLink>
Das Löschen eines Kunden wird als Ajax-Anfrage ausgeführt. Der View-Access-Scope der Bean bedeutet, dass die Konversation der Bean so lange offen bleibt, bis nicht mehr auf sie zugegriffen wird.
@Named @ViewAccessScoped
public class CustomerListBean implements Serializable {
  @Inject
  private CustomerService customerService;
  private List<Customer> customerList;
  @PreRenderView
  public void preRenderView() {
    customerList = customerService.findAll();
  }
  public List<Customer> getCustomerList() {
    return customerList;
  }
  public void deleteCustomer(Customer customer) {
    customerService.delete(customer);
  }
}
Wir haben im Zuge der Änderungen in MyGourmet 17 alle Operationen für Objekte vom Typ Customer im Interface CustomerService zusammengefasst. Die Implementierung CustomerServiceImpl dieses Interface steht als CDI-Bean zur Verfügung. Listing MyGourmet 17: Page-Bean der Kunden-Übersichtsseite zeigt, wie die Abhängigkeit zum Service mit @Inject definiert wird.
Listing MyGourmet 17: View-Config zeigt Teile der View-Config für MyGourmet 17 im Interface View . Aus showCustomer.xhtml wird zum Beispiel die Klasse ShowCustomer . Da alle Klassen View implementieren, muss dort über basePath in der Annotation @Page der Pfad überschrieben werden. Ohne diese Anpassung würde CODI davon ausgehen, dass die Seiten im Verzeichnis /view liegen und die View-IDs dementsprechend anpassen. Alternativ könnten wir natürlich die XHTML-Dateien in das Verzeichnis /view verschieben. Die Definition der Page-Beans finden Sie ebenfalls in der View-Config in Listing MyGourmet 17: View-Config .
@Page(basePath = "")
public interface View extends ViewConfig {
  @Page @PageBean(AddCustomerBean.class)
  public class AddCustomer1 implements View {}
  @Page @PageBean(AddCustomerBean.class)
  public class AddCustomer2 implements View {}
  @Page @PageBean(CustomerListBean.class)
  public class CustomerList implements View {}
  @Page @PageBean(CustomerBean.class)
  public class ShowCustomer implements View {}
}
Die Detailseite showCustomer.xhtml ist über eine h:commandLink -Komponente in der Übersichtsseite erreichbar. Bei einem Klick auf den Link wird die Methode showCustomer der Bean CustomerBean mit der ID des Kunden aufgerufen. Die Methode lädt den Kunden und gibt die View-Config der Detailseite für die Navigation zurück. Die Klasse AddressBean ist in der Klasse CustomerBean aufgegangen. Listing MyGourmet 17: Page-Bean der Kundenansichten zeigt die relevanten Teile der Klasse CustomerBean .
@Named @ViewAccessScoped
public class CustomerBean extends CustomerBeanBase {
  @Inject
  private CustomerService customerService;
  public Class<? extends ViewConfig> showCustomer(long id) {
    this.customer = customerService.findById(id);
    return View.ShowCustomer.class;
  }
}
Wenn der Benutzer auf die Detailseite navigiert, wird eine Instanz der Bean CustomerBean im View-Access-Scope inklusive der Konversation erstellt. Die Konversation bleibt aktiv, bis während einer Anfrage keine Zugriffe mehr auf die Bean CustomerBean erfolgen.
Der Wizard zum Anlegen eines neuen Kunden ist ebenfalls von der Übersichtsseite aus erreichbar. Der Ablauf besteht aus den beiden Ansichten addCustomer1.xhtml zur Eingabe der Basisdaten des Kunden und addCustomer2.xhtml zur Eingabe einer Adresse. Die Bean AddCustomerBean ist die Page-Bean für beide Ansichten und liegt im Conversation-Scope von CODI. Listing MyGourmet 17: Page-Bean des Wizards zum Anlegen eines Kunden zeigt die relevanten Teile der Klasse AddCustomerBean . Die Bean bekommt den Service CustomerService injiziert, der zum Erzeugen einer neuen Customer -Instanz in der Methode createCustomer verwendet wird. Da diese Methode mit @InitView annotiert ist, wird sie von CODI nach der Restore-View-Phase aufgerufen, wenn der Lebenszyklus für eine der beiden verknüpften Ansichten ausgeführt wird. So ist gewährleistet, dass immer eine Instanz der Klasse Customer existiert.
@Named @ConversationScoped
public class AddCustomerBean extends CustomerBeanBase {
  @Inject
  private CustomerService customerService;
  @Inject
  private Conversation conversation;
  @InitView
  public void createCustomer() {
    if (customer == null) {
      customer = customerService.createNew();
    }
  }
  public Class<? extends ViewConfig> save() {
    customerService.save(customer);
    conversation.close();
    return View.CustomerList.class;
  }
  public Class<? extends ViewConfig> cancel() {
    conversation.close();
    return View.CustomerList.class;
  }
}
Da die Bean AddCustomerBean im Conversation-Scope liegt, müssen wir uns selbst um das Beenden der Konversation kümmern. Dazu wird in den Action-Methoden save() und cancel() die Methode close() auf der Konversation aufgerufen, die wir uns von CDI injizieren lassen.