Java Server Pages (JSP) |
REST |
Java Server Faces (JSF) ist eine Jakarta/Java Enterprise-Technologie für das User-Interface. Java Server Faces findet seinen Einsatz bei der Erstellung von dynamischen Webseiten und ist dort vergleichbar mit den Java Server Pages (JSP).
Im Gegensatz zu Java Server Pages bettet JSF keinen Java-Code in den HTML-Code ein. Stattdessen definiert sie Oberflächenelemente im HTML-Code (Facelet), die sich auf Elemente in einem Datenobjekt befinden, das als Backing Bean bezeichnet wird.
Die Benutzeraktivitäten auf der Oberfläche verändern das zugrundeliegende Objekt und umgekehrt. Dazu verbindet das Facelet die Oberflächenkontrollelemente direkt mit den Attributen der Backing Bean. Ebenso kann ein Facelet durch Buttons oder Links die Methoden der Backing Bean aufrufen. Für diese Zugriffe und Aufrufe definiert Java Server Faces eigene Tags.
- Beispiel: Addieren mit JSF
- Spezielle JSF-Tags
- Der Zugriff von Facelets auf ihre Backing Bean
- Datenobjekt als Eingabe und Liste
- Kombination von Backing Beans: Lieferung und Kunde
- Zugriffe auf die HTTP-Umgebung
- Templating
- JSF und Eclipse
Beispiel: Addieren mit JSF
Das erste Beispiel ist eine JSF-Seite, die zwei Zahlen addieren soll. Als Entwicklungsumgebung wird Eclipse mit Glassfish vorausgesetzt.
- Zunächst wird ein Dynamic Web Project angelegt. File | New | Dynamic Web Project
- Im dem erscheinenden Dialog wird der Button Modify gedrückt. Er führt zu einem weiteren Dialog, in dem die Konfiguration festgelegt wird.
- Hier wird Java Server Faces mit einem Haken versehen.
Backing Bean
Die Backing Bean, die die Basis für das Facelet darstellt, ist im Grunde eine einfache Klasse mit Attributen für jedes Oberflächenelement und Methoden für jedes Event, das allerdings als Backing Bean angemeldet wird. Die Erzeugung geschieht unter Eclipse also wie bei jeder anderen Klasse auch:- Projekt mit der rechten Maustaste anklicken und New | Class wählen.
- Als Namen verwenden Sie in diesem Beispiel CalcBackBean.
CalcBackBean wird durch die Annotation @Named als Backing Bean gekennzeichnet. @ManagedBean ist inzwischen veraltet.
Mit dem Scope wird die Gültigkeit der Bean beschrieben. Der Import muss aus javax.enterprise.context stammen. javax.faces.beans ist deprecated und führt ab Glassfish 5 zu einem Deploy-Fehler.
- @RequestScoped
- @SessionScoped
- @ApplicationScoped
import java.io.Serializable; import javax.enterprise.context.SessionScoped; import javax.inject.Named; @Named @SessionScoped public class CalcBackBean implements Serializable { private static final long serialVersionUID = 199543L; private int a; private int b; private int summe=12; public void setA(int a) { this.a = a; } public int getA() { return a; } public void setB(int b) { this.b = b; } public int getB() { return b; } public int getSumme() { return summe; } public void calc() { summe = a + b; } }Die Attribute a und b sind die Summanden. Sie müssen private sein. Für jedes Feld, das ausgegeben werden soll, muss ein Getter existieren. Alle Elemente, für die es ein Eingabefeld gibt, benötigen einen Getter und einen Setter.
Für jeden Button der Oberfläche wird eine Methode (hier calc) definiert. Sie hat keinen Parameter und führt hier die Addition der beiden Variablen aus.
Die Methode kann einen String als Rückgabewert zurückgeben, in dem die Seite angegeben wird, zu der der Client navigieren soll, nachdem die Aktion ausgeführt wurde.
public String calc() { summe = a + b; return "index.xhtml"; }In unserem Beispiel wäre das aber weniger sinnvoll, da der Benutzer dann das Ergebnis der Addition nicht sehen könnte.
Backing Bean in faces-config.xml anmelden
In der JSF-Konfigurationsdatei werden die ManagedBeans erfasst. Eclipse stellt für deren Pflege einige Dialoge zur Verfügung. Diese werden bei Doppelklick auf die Datei faces-config.xml geöffnet, die sich im Projekt unter dem Verzeichnis WebContent/WEB-INF befindet.In der Datei faces-config.xml müssen die Backing Bean angemeldet werden, damit sie von den Facelets verwendet werden können. In ihrem Ausgangszustand sieht die Datei folgendermaßen aus:
<?xml version="1.0" encoding="UTF-8"?> <faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_3.xsd" version="2.3"> </faces-config>
Unter dem Editor gibt es mehrere Tabs. Um eine Backing Bean anzumelden, ist es am einfachsten, folgende Schritte durchzuführen.
- Die Tab Managed Bean auswählen.
- In dem erscheinenden Dialog auf den Button Add klicken.
- Der Qualified Class Name wird durch Klick auf Browse bestimmt.
- In die Eingabefeld CalcBackBean eintippen. In der Liste erscheint die Klasse. Diese anwählen und auf OK klicken.
- Zum Abschluss Finish klicken.
Letztlich entsteht eine XML-Datei faces-config.xml, die unter /WebContent/WEB-INF zu finden ist. Gegebenenfalls können noch direkte Änderungen unter der Lasche Source durchgeführt werden.
<?xml version="1.0" encoding="UTF-8"?> <faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_3.xsd" version="2.3"> <managed-bean> <managed-bean-name>calcBackBean</managed-bean-name> <managed-bean-class>CalcBackBean</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean> </faces-config>
Anlegen eines Facelets
Facelets werden in XHTML kodiert. XHTML besteht auf einer saubereren Verwendung der Tags als HTML. Dort wird das fehlende Schließen eines Tags lässig ignoriert. Für das Facelet wird also eine XHTML-Datei angelegt.- Das Projekt mit der rechten Maustaste anklicken.
- Aus dem Kontextmenü New | HTML-File wählen.
- Im Dialog erhält die Datei einen Namen, beispielsweise index.xhtml.
- Durch Klicken auf Next wird der Typ näher spezifiziert.
- Aus den Templates aussuchen: New XHTML File (transitional)
- Mit Finish wird die Erzeugung der Datei abgeschlossen.
Die Dateien enden zwar auf .xhtml, werden aber in einem Link mit der Endung .jsf aufgerufen.
Arbeiten mit dem Web-Editor
Sie können unter Eclipse einen Web-Editor verwenden, der es ermöglicht, die Elemente per Drag & Drop zu setzen.- Schließen Sie die Editor-Ansicht der XHTML-Datei.
- Klicken Sie die Datei im Projekt mit der rechten Maustaste an.
- Wählen Sie Open with | Web Page-Editor.
- Nun kann über Window | Show View | Other | General | Palette eine Palette erstellt werden, aus der die Elemente des Bildschirms gezogen werden können.
In der Palette befinden sich alle möglichen Elemente, die per Drag und Drop in den Bildschirm gezogen werden können. Für den Anfang befinden sich die wichtigsten Elemente unter JSF-HTML.
Anpassen des Additions-Facelets im Editor
Das Additions-Facelet können Sie mit folgenden Elementen schnell per Drag und Drop zusammenklicken:- JSF-HTML | Form in den Body ziehen.
- JSF-HTML | Panel Grid in die Form ziehen
- JSF-HTML | Output Text in das linke obere Feld ziehen
- JSF-HTML | Text Input in das rechte obere Feld ziehen
- Dasselbe für die nächsten beiden Zeilen
- JSF-HTML | Command Button unten links oder rechts.
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html"> <h:head> <title><h:outputText value="Wir addieren" /></title> </h:head> <h:body> <h:form> <h:panelGrid columns="2"> <h:outputText value="A = " /> <h:inputText value="#{calcBackBean.a}"> </h:inputText> <h:outputText value="B = " /> <h:inputText value="#{calcBackBean.b}"> </h:inputText> <h:outputText value="Summe = " /> <h:outputText value="#{calcBackBean.summe}" /> <h:outputText value="Los geht's" /> <h:commandButton value="OK" action="#{calcBackBean.calc}" /> </h:panelGrid> </h:form> </h:body> </html>Es ist zu erkennen, dass die Elemente der Backing Bean per #{bean.element} erreicht werden.
- Das Input-Element wird mit der Variablen a in CalcBackBean verbunden.
<h:outputText value="A = " /> <h:inputText value="#{calcBackBean.a}"> </h:inputText>
- Der Button löst die Methode calc in CalcBackButton aus.
<h:commandButton value="OK" action="#{calcBackBean.calc}" />
- Die Variable summe aus CalcBackBean wird angezeigt.
<h:outputText value="Summe = " /> <h:outputText value="#{calcBackBean.summe}" />
Starten der JSF-Seite
Sie können die XHTML-Datei mit der rechten Maustaste anklicken und mit Run As | Run on Server starten. Funktioniert das nicht, steht die XHTML-Datei eventuell nicht unter WebContent im Projekt.Obwohl die Datei im Projekt als index.xhtml abgelegt ist, wird sie über den Browser als index.jsf angesprochen.
Spezielle JSF-Tags
Attribute benutzerdefinierter Tags werden in der Property der UI-Komponente deklariert. Um die Tags, die mit h: beginnen aufzulösen, muss die Definition xmlns:h="http://xmlns.jcp.org/jsf/html" in das html-Tag eingebunden sein. Mit <h: beginnende Tags bilden HTML-Elemente nach.
- <h:head> bis </h:head>
Bildet den Head-Bereich der Seite. - <h:body> bis </h:body>
Bildet den Body der Seite. - <h:form> bis </h:form>
Definiert ein Fomular in JSF. Im Gegensatz zu HTML-Formularen wird hier weder die Aktion noch die Methode hinterlegt. - <h:panelGrid> bis </h:panelGrid>
Definiert eine Tabelle. Mit dem Attribut columns wird die Anzahl der Spalten festgelegt. Die Inhalte werden dann einfach hintereinander eingefügt und automatisch auf die Spalten verteilt. - <h:commandLink action="antwort.xhtml" value="Antwort" />
Damit wird ein Link auf antwort.xhtml gesetzt, der mit Antwort beschriftet ist.
Der Zugriff von Facelets auf ihre Backing Bean
JSF leiten den Zugriff auf Elemente durch ein Doppelkreuz (Lattenzaun, Raute) ein und umschließen sie mit geschweiften Klammern.
- <h:inputText value="#{kundenCtrl.name}" />
Eingabefeld, das auf das Attribut name der Backing Bean KundenCtrl speichert. - h:inputText kann auch zur Eingabe eines Datums verwendet werden:
<h:outputLabel value="Geburtstag: "/> <h:inputText value="#{gast.gebtag}"> <f:convertDateTime pattern="dd/mm/yyyy"/> </h:inputText>
- <h:inputTextArea value="#{bean.attribut}" id="idName" />
Eingabe eines Textbereichs - <h:outputText value="#{bean.attribut}" />
zeigt den Inhalt des Attributs attribut der Backing Bean bean an. - <h:commandButton action="{#kundenCtrl.speicher}" />
Ein Button, der bei Auslösung die Methode speicher in der Klasse KundenCtrl aufrufen wird. - <h:selectBooleanCheckbox value="#{bean.attribut}" />
Checkbox - <h:dataTable value="#{bean.attribut}" />
Tabelle mit Daten - <h:selectOneMenu value="#{bean.attribut}" id="idName" />
Dropbox.- Attribut: value="#{bean.attribut}" gibt zu bearbeitende Attribut der Backing Bean an.
- Attribut: converter="Converter" verweist auf eine Klasse, die mit @FacesConverter markiert ist. Damit lassen sich Objekte in anzuzeigende Strings wandeln.
- <f:selectItem itemLabel="Beschriftung" />
- <f:selectItems value="#{bean.attributliste}" />
Datenobjekt als Eingabe und Liste
Normalerweise werden Backing-Beans nicht als Taschenrechner eingesetzt, sondern repräsentieren ein Datenobjekt. Typischerweise werden JSF für die Interaktion mit einem Objekt erstellt. Dabei ergeben sich zwei Dialoge:- Ein Dialog zur Eingabe, Änderung oder Darstellung eines Objekts
- Eine Liste für die Übersicht mehrerer Objekte
Projekt
Zur Realisierung wird ein Projekt benötigt.
- Es wird wieder ein Dynamic Web Project angelegt.
- Unter Modify wird wieder Java Server Faces aktiviert.
- Wir benötigen eine Backing Bean: Wohnung
- Diese muss in der faces-config.xml eingetragen werden.
- Wir erstellen ein Facelet.
Backing Bean
Wohnung
Eine Wohnung hat folgende Attribute:- Eine Bezeichnung oder ein Name: String bez
- Eine Anzahl von Quadratmetern: int qm
- Eine Miete: double miete;
import java.io.Serializable; import javax.enterprise.context.ApplicationScoped; import javax.inject.Named; @Named @ApplicationScoped public class Wohnung implements Serializable { private static final long serialVersionUID = 3020832L; private String bez; private int qm; private double miete; }Um für die privaten Attribute die passenden Getter und Setter zu machen, kann Eclipse helfen.
- In den Editor klicken, Source|Generate Getters and Setters aus dem Menü auswählen.
- Im Dialog den Button Select All drücken
- Bei serialVersionUID den Haken wieder entfernen
- Klick auf Generate erzeugt automatisch alle Getter- und Settermethoden.
WohnungsListe
Für die Wohnungsliste wird eine statische ArrayList in der Klasse Wohnung angelegt. Für den Zugriff aus dem Facelet wird eine nicht-statische get-Methode benötigt.private static ArrayList<Wohnung> liste = new ArrayList<>(); public ArrayList<Wohnung> getListe() { return liste; }
Eintrag in faces-config.xml
In der faces-config.xml wird die Wohnung eingetragen. Unter der Lasche Source müsste sie zum Schluss so aussehen. Im Zweifelsfall nacheditieren.<?xml version="1.0" encoding="UTF-8"?> <faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_3.xsd" version="2.3"> <managed-bean> <managed-bean-name>wohnung</managed-bean-name> <managed-bean-class>Wohnung</managed-bean-class> <managed-bean-scope>application</managed-bean-scope> </managed-bean> </faces-config>
Facelet für die Wohnung
Zunächst wird ein normales Formular für die Eingabe erstellt.<h:form> <h:panelGrid border="1" columns="2"> <h:outputText value="Name: "></h:outputText> <h:inputText value="#{wohnung.bez}"></h:inputText> <h:outputText value="qm: "></h:outputText> <h:inputText value="#{wohnung.qm}"></h:inputText> <h:outputText value="Miete: "></h:outputText> <h:inputText value="#{wohnung.miete}"></h:inputText> <h:outputText value="Übernehmen "></h:outputText> <h:commandButton value="OK" action="#{wohnung.insert}"/> </h:panelGrid> </h:form>Der Button OK ruft die Methode insert auf.
Die Methode insert erzeugt ein neues Objekt und kopiert \ident{this} hinein. Das ist erforderlich, weil ansonsten alle Referenzen in der Liste auf dasselbe Objekt verweisen. Die Liste wird dann sehr langweilig.
public void insert() { Wohnung w = new Wohnung(); w.setBez(bez); w.setQm(qm); w.setMiete(miete); liste.add(w); }Über oder unter das Eingabe-Formular wird ein DataTable gesetzt.
<h:dataTable value="#{wohnung.liste}" var="wng"> <h:column> <f:facet name="header">Name</f:facet> #{wng.bez} </h:column> <h:column> <f:facet name="header">qm</f:facet> #{wng.qm} </h:column> <h:column> <f:facet name="header">Miete</f:facet> #{wng.miete} </h:column> <h:column> <f:facet name="header">Aktion</f:facet> <h:commandButton value="Löschen" action="#{wng.delete}"/> </h:column> </h:dataTable>Wichtig ist die Einfügung der xmlns-Zeilen im HTML-Tag.
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets">
- h:dataTable definiert in value das Attribut der Tabelle. Das muss also ein Array oder eine List bzw. ArrayList sein.
- h:dataTable definiert in var den Namen, über den in jeder Zeile auf das Element der Tabelle zugegriffen werden kann.
- Innerhalb der h:dataTable werden die h:column für jede Spalte definiert.
- In f:facet wird der Kopf der Spalte beschrieben. Das Attribut name enthält die Überschrift.
- Ansonsten enthält h:column das Attribut des Elements.
- Die Spalte kann auch ein commandButton enthalten, wie hier zum Löschen des Elements.
Also muss die Methode nur das aktuelle Objekt aus der Liste entfernen.
public void delete() { liste.remove(this); }
Einbinden von CSS
- Legen Sie im Projekt unter dem Verzeichnis WebContent einen Folder resources und darunter einen Folder css an.
- Darin erstellen Sie mit New|File ihre CSS-Datei, etwa table.css
.table { border-collapse:collapse; } .header{ background:none repeat scroll 0 0 #EEEEEE; border-bottom:1px solid #BBBBBB; } .odd-row{ background:none repeat scroll 0 0 #FFFFFFF; border-top:1px solid #BBBBBB; } .even-row{ background:none repeat scroll 0 0 #DDDDDD; border-top:1px solid #BBBBBB; }Im Header wird die CSS-Datei in die JSF eingebunden.
<h:head> <h:outputStylesheet library="css" name="table.css" /> </h:head>Die CSS-Classes werden in der h:dataTable-Tag angesprochen.
<h:dataTable value="#{wohnung.liste}" var="wng" styleClass="table" headerClass="header" rowClasses="odd-row,even-row" >
Und hier das Ergebnis:
Kombination von Backing Beans: Lieferung und Kunde
In manchen Fällen haben Klassen Fremdschlüssel. So geht eine Lieferung an einen Kunden. Es gelten also die Klassen Lieferung und Kunde.Im ersten Schritt wird der Kunde als einfacher String in der Klasse Lieferung hinterlegt.
import java.io.Serializable; import javax.enterprise.context.ApplicationScoped; import javax.inject.Named; @Named @ApplicationScoped public class Lieferung implements Serializable { private static final long serialVersionUID = 2134L; private String artikelnr; private double menge; private String kunde; // Es folgen die Getter und Setter und die Methode für den Button
Hier müssen die benötigten Getter und Setter definiert werden. Außerdem muss die Backing Bean in der faces-config.xml angemeldet werden, wie oben beschrieben.
Das Facelet greift auf die Attribute der Backing-Bean zu:
<h:head> <title><h:outputText value="Lieferung:" /></title> </h:head> <h:body> <h:form> <h:panelGrid columns="2"> <h:outputText value="Artikel " /> <h:inputText value="#{lieferung.artikelnr}"> </h:inputText> <h:outputText value="Menge: " /> <h:inputText value="#{lieferung.menge}"> </h:inputText> <h:outputText value="Kunde: " /> <h:inputText value="#{lieferung.kunde}" /> <h:outputText value="Speichern" /> <h:commandButton value="OK" action="#{lieferung.speichern}" /> </h:panelGrid> </h:form> </h:body>
Bean-Injection
Im nächsten Schritt soll der Kunde allerdings nicht als einfacher String repräsentiert werden, sondern über eine eigene Bean Kunde. Dazu wird Kunde in die Bean Lieferung injected.import java.io.Serializable; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import javax.inject.Named; @Named @ApplicationScoped public class Lieferung implements Serializable { private static final long serialVersionUID = 2134L; private String artikelnr; private double menge; @Inject private Kunde kunde; public Kunde getKunde() { return kunde; } public void setKunde(Kunde kunde) { this.kunde = kunde; } // Der Rest wie gehabt...
Die Bean Kunde sieht der Bean Lieferung sehr ähnlich.
import java.io.Serializable; import javax.enterprise.context.Dependent; import javax.inject.Named; @Named @Dependent public class Kunde implements Serializable { private static final long serialVersionUID = 1L; private long id; private String name; private String ort; private String telefon; // Auch hier Getter und Setter...Die Annotation @Dependent sorgt dafür, dass der Scope der Bean, in die injected wird, übernommen wird. Die injected Bean sollte keinen weiteren Scope haben als die einbindende Bean.
Im Formular des Facelets wird nun über lieferung auf kunde und von dort auf dessen Attribut name referenziert.
<h:outputText value="Kunde: " /> <h:inputText value="#{lieferung.kunde.name}" />
Zugriffe auf die HTTP-Umgebung
URL-Parameter auslesen
Über den Link können Werte auch bei GET-Kommandos übergeben werden. Beispiel:<a href="index.xhtml?wert=42">Link mich!</a>Eine JSF liest diesen Parameter über die Map param aus:
Die Antwort ist #{param['wert']}
Sitzungsinformationen
- #{header['User-Agent']} liefert Informationen über den verwendeten Browser.
- #{session.id} kann die Session eindeutig identifizieren.
- #{sessionScope['userName']} liefert den Benutzernamen der Session, sofern gesetzt.
- #{cookie['MEINCOOKIE'].value} liefert den Wert des Cookies MEINCOOKIE.
Templating
JSF-Seiten sollten über die Anwendung hinweg gleichmäßig aussehen. Das unterstützt JSF durch Templating. Ein Template enthält Platzhalter für andere ein JSF-Dokumente. Jenes JSF-Dokument wiederum bezieht sich auf das Template, in das es eingebettet werden will.JSF und Eclipse
Für das Nachvollziehen der JSF-Beispiele benötigen Sie ein Eclipse für JEE mit Glassfish. Tomcat wird an dieser Stelle nicht empfohlen, da Tomcat Nachinstallationen benötigt, um Java Server Faces zu realisieren.Manche Deploy-Fehler sind folgendermaßen zu beseitigen.
- Server mit der rechten Maustaste anklicken.
- Monitoring | Properties
- Glassfish: Haken setzen bei Use JAR archives for deployment
JEE-Literatur
Alexander Salvanos Professionell entwickeln mit Java EE 8Marcus Schießer, Martin Schmollinger: Workshop Java EE 7
Etwas abstrakter: Dirk Weil: Java EE 7: Enterprise-Anwendungsentwicklung leicht gemacht
Das Buch enthält über 100 Seiten über JEE: Arnold Willemer: Java Alles-in-einem-Band für Dummies