Prof. Dr. Stephan Kleuker
209Komponentenbasierte Software-Entwicklung
4. JEE /JSF / EJB / CDI
4.1 Grundlagen
4.2 Einführendes nulltes Beispiel
4.3 Validierung
4.4 JSF-Lebenszyklus
4.5 JSF mit EJB und JPA
4.6 Detaillierteres Beispiel: Mitarbeiterverwaltung
4.7 Testen von JEE-Programmen
4.8 Templates und Komponenten
Prof. Dr. Stephan Kleuker
210Komponentenbasierte Software-Entwicklung
Literatur
• E.Burns, C. Schalk, JavaServer Faces 2.0: The Complete Reference, Mc Graw Hill, New York, 2010
• M. Marinschek, M. Kurz, G. Müllan, JavaServer Faces 2.0, dpunkt, Heidelberg, 2010 (im Wesentlichen auch: http://tutorials.irian.at/jsf/semistatic/introduction.html)
• D. Geary, C. Horstmann, Core JavaServer Faces, 3. Auflage, PrenticeHall, USA, 2010
• Standard: Sun, JSR 344: JavaServer Faces 2.2, http://jcp.org/aboutJava/communityprocess/final/jsr344/index.html
• M. Çalışkan, O. Varaksin, PrimeFaces Cookbook - Second Edition, Packt Publishing, Birmingham UK, 2015
• Oracle, JEE7 Tutorial, https://docs.oracle.com/javaee/7/tutorial/https://docs.oracle.com/javaee/7/tutorial/jsf-intro.htm#BNAPH
• Bücher nutzen teilweise Eclipse und JBoss/WildFly; nicht notwendig, funktioniert fast alles mit Netbeans und Glassfish / Apache
Prof. Dr. Stephan Kleuker
211Komponentenbasierte Software-Entwicklung
4.1 Grundlagen
Prof. Dr. Stephan Kleuker
212
Komponenten und SW-Architektur
Komponentenbasierte Software-Entwicklung
DB
ZugriffJPA
Fachklassen
Zugriffskoordination EJB
Fachlogik
View-Repräsentation Scoped Beans
Controller
View JSF
Prof. Dr. Stephan Kleuker
213Komponentenbasierte Software-Entwicklung
HTTP-Ablauf
• Client: Request
– get, post, head, put, ... URL HTTP1.x
– Header Info: Accept, Cookie, ...
– Body: Post-Parameter
• Server: Response
– Statusmeldung: HTTP1.x 200 OK, oder 404 Error
– Header Info: Content-type, set-cookie, ...
– Body: Dokument
• Verbindungsabbau
• Protokoll ist zustandslos/gedächtnislos; Client wird bei erneutem Request ohne Trick nicht als Bekannter erkannt
Prof. Dr. Stephan Kleuker
214Komponentenbasierte Software-Entwicklung
Web-Server mit Container nutzen (JEE)
• Servlet: Wortkreation aus den Begriffen „Server“ und „Applet“, (serverseitiges Applet)
• Web-Server leitet HTTP-Request an Servlet weiter
• Servlet kann Antwort (HTML-Code) berechnen
• Servlet kann Anfrage-Informationen und Serverumgebung nutzen
• Servlet kann mit anderen Servlets kommunizieren
Container
Prof. Dr. Stephan Kleuker
215Komponentenbasierte Software-Entwicklung
Motivation für JSF
• Die Widerverwendbarkeit von Servlets ist gering
• Innerhalb jeder JSP (Java ServerPage) bzw. jedes Servlets müssen ähnliche Schritte ausgeführt werden
• Nur ein Teil der Applikationslogik kann in Tag-Bibliotheken gekapselt werden
• Workflow ist in der Applikationslogik versteckt, dadurch schwer nachvollzierbar
• ab JSF 2.0 sehr flexible Gestaltungsmöglichkeiten, u. a. AJAX-Einbettung
• Hinweis: wir betrachten nur Grundkonzepte (-> mehr evtl. Semesteraufgabe)
Prof. Dr. Stephan Kleuker
216Komponentenbasierte Software-Entwicklung
Web-Server mit JSF- (und EJB-) Unterstützung
• Beispiele:
– Apache Tomcat / TomEE
– BEA WebLogic Server
– IBM WebSphere (Apache Geronimo)
– JBoss Wildfly
– Oracle WebLogic
– Glassfish (Oracle, Referenzimplementierung)
Unterschiedliches Tempo bei der Unterstützung neuer JEE-Versionen
Prof. Dr. Stephan Kleuker
217Komponentenbasierte Software-Entwicklung
Konzeptübersicht
Input-Komponente beschrieben in
XHTML...
Web-Seite in XHTML
Output-Komponente beschrieben in
XHTML...
Web-Seite in XHTML
Modell
Java-Programmim Container
im Server
event
leitet auf
Folgeseite
liest
Prof. Dr. Stephan Kleuker
218Komponentenbasierte Software-Entwicklung
XHTML
• Idee: HTML nicht XML-konform und zu viele Freiheiten
• XHTML in fast allen Elementen wie HTML
• XHTML basierend auf XML-Schema leichter zu verarbeiten
• Unterschiede zu HTML (Ausschnitt)
– Tags und Attribute müssen klein geschrieben werden
– Attributwerte immer in Anführungsstrichen boarder="7"
– korrekte XML-Syntax: <br/> nicht <br>
– generell saubere Terminierung <input ... />
• XHTML2 wegen HTML5 eingestellt (verschwimmt damit) http://www.w3.org/2009/06/xhtml-faq.html
• Standard: http://www.w3.org/TR/xhtml1/
Prof. Dr. Stephan Kleuker
219Komponentenbasierte Software-Entwicklung
Web-GUI-Komponenten
• JSF bietet alle klassischen GUI-Komponenten zur Darstellung und Bearbeitung an
• Grundidee: Komponenten schicken bei Veränderungen Events
• Nutzung von MVC2
• Komponenten als XHTML eingebettet
<h:inputText id="imname" value="#{modul.name}“ required="true"/>
<h:outputText id="omname" value="#{modul.name}"/>
• Kontakt zum Java-Programm über Methodenaufrufe (lesend und aufrufend, z. B. modul.setName(...), modul.getName()
• große Komponenten können aus kleineren komponiert werden
Prof. Dr. Stephan Kleuker
220
Elementare Aufgabe: Eingabeseite mit Ausgabeseite
WebSeite
Modulname:
Modulnummer:
Modulname: <Ausgabe>
Modulnummer: <Ausgabe>
Abschicken Zur Eingabe
WebSeite
Eingabefeld Ausgabefeld
="#{modul.name}" ="#{modul.name}"
@Named("modul")
@RequestScoped
public class Modul implements Serializable {
private String name;
Managed Bean(Controller)
Knopf
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
221
Projekt einrichten (1/2)
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
222
Projekt einrichten (2/2)
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
223Komponentenbasierte Software-Entwicklung
4.2 Nulltes JSF-Beispiel (1/11)
• Aufgabe: Modul (Name + Nummer) eingeben und auf zweiter Seite wieder ausgeben
• Beispiel zeigt:
– Konzept der XHTML-Nutzung
– erste JSF-Befehle
– Seitenmanövrierung durch JSF
• 0. Beispiel zeigt nicht:
– ordentliche Softwarearchitektur (Controller und Model trennen)
– Validierungsmöglichkeiten für Eingaben
– Layout-Gestaltung
– viele weitere coole Dinge
Prof. Dr. Stephan Kleuker
224
<?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>Moduleingabe</title>
</h:head>
<h:body>
<h:form id="form" >
<h:outputText value="Modulname "/>
<h:inputText id="mname" value="#{modul.name}"
required="true"/><br/>
<h:outputText value="Modulnummer "/>
<h:inputText id="mnr" value="#{modul.nr}"
required="true"/><br/>
<h:commandButton value="Abschicken"
action="#{modul.uebernehmen}"/>
</h:form>
</h:body>
</html>
Nulltes JSF-Beispiel (2/11) - Start index.xhtml
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
225
Nulltes JSF-Beispiel (3/11) - Analyse der Startseite
• Einbinden der JSF-Bibliotheken<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f ="http://xmlns.jcp.org/jsf/core" >
• Nutzung von Standard XHTML (vereinfacht, da so als Standardnamensraum gesetzt)
• enge Verwandtschaft HTML zu XHTML (<h:form>)
• für value="#{modul.name}" offene Fragen:
– was ist modul? ( -> Managed Bean)
– warum #? (Trennung von ausführbaren Elementen)
– was passiert bei modul.name? (set/get abhängig von in/out)
• Ausblick: action="#{modul.uebernehmen}", echtes Event-Handling (Methodenaufruf)
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
226
Nulltes JSF-Beispiel (4/11) - Ausgabe ausgabe.xhtml<?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>Modulausgabe</title>
</h:head>
<h:body>
<h:form id="form">
<h:outputText value="Modulname: "/>
<h:outputText id="mname" value="#{modul.name}"/> <br/>
<h:outputText value="Modulnummer: "/>
<h:outputText id="mnr" value="#{modul.nr}"/><br/>
<h:commandButton value="Zur Eingabe"
action="#{modul.eingeben}"/>
</h:form>
</h:body>
</html>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
227
Nulltes JSF-Beispiel (5/11) - Managed Bean [1/3]
package entities;
import java.io.Serializable;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
@Named("modul") // nur im Minimalbeispiel hat eine Entitaet
@RequestScoped // einen zugeordneten Scope
public class Modul implements Serializable {
private static final long serialVersionUID = 1L;
private int nr;
private String name;
public Modul(){}
public Modul(int nr, String name) {
this.nr = nr;
this.name = name;
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
228
Nulltes JSF-Beispiel (6/11) - Managed Bean [2/3]
public String uebernehmen(){
return "/ausgabe.xhtml";
}
public String eingeben(){
return "/index.xhtml";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNr() {
return nr;
}
public void setNr(int nr) {
this.nr = nr;
}
//Manövrieren
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
229
Nulltes JSF-Beispiel (7/11) - Managed Bean [3/3]@Override // generieren lassen
public boolean equals(Object object) {
if (object==null || !(object instanceof Modul))
return false;
Modul other = (Modul) object;
if (this.nr != other.nr || !this.name.equals(other.name))
return false;
return true;
}
@Override // generieren lassen
public int hashCode() {
int hash = 5;
hash = 47 * hash + this.nr;
hash = 47 * hash
+ (this.name != null ? this.name.hashCode() : 0);
return hash;
}
@Override
public String toString() {return name+"("+nr+")";}
}Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
230
Nulltes JSF-Beispiel (8/11) - web.xml [1/2]• Konfigurationsdatei für Servlets (hier benötigt; wird generiert)<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1"
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-app_3_1.xsd">
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
231
Nulltes JSF-Beispiel (9/11) - web.xml [2/2]
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
2
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>faces/index.xhtml</welcome-file>
</welcome-file-list>
</web-app>
fehlt z. B. gesamte Fehlercodebehandlung
in glassfish-web.xml (Glassfish-spezifisch) steht, wenn existent, z. B.<context-root>/vlJSFNulltesBeispiel</context-root>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
232
Nulltes JSF-Beispiel (10/11) - Projektstruktur
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
233
Nulltes JSF-Beispiel (11/11) - Ergebnis
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
234
beans.xml
• Datei garantiert korrekte Abarbeitung von Annotationen
• Datei ist Konfigurationsdatei von Contexts- and DependencyInjection (CDI)
• einige Teile CDI werden in Veranstaltung nebenbei eingeführt
• Datei muss in Projekten selbst erzeugt werden
• <?xml version="1.0" encoding="UTF-8"?><beans 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/beans_1_1.xsd"
bean-discovery-mode="all">
</beans>
Komponentenbasierte Software-Entwicklung
Wert muss von Hand von „annotated“ auf „all“ gesetzt werden
Prof. Dr. Stephan Kleuker
235
beans.xml in NetBeans erzeugen
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
236
Richtige Klassen und Annotationen nutzen!• bei Korrekturvorschlägen immer auch richtige Klasse
achten, steht nicht immer oben oder ist ausgewählt!!!
• falsche Klasse führt teilweise zu extrem schwer zu findenden Fehlern
• Historisch sind diese Klassen oft verwandt und ältere Ansätze werden nicht verschwinden
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
237
Einschub: korrekte Pfadangabe
• Browser zeigt immer vergangene Seite
• wenn nicht gewünscht, dann Manövrierungsstring ändern
• ist etwas langsamer (warum, später genauer)
• Scope auf @SessionScoped setzenpublic String uebernehmen(){
return "/ausgabe.xhtml?faces-redirect=true";
}
public String eingeben(){
return "/index.xhtml?faces-redirect=true";
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
238Komponentenbasierte Software-Entwicklung
Basisprinzip der Applikationssteuerung
• Im Controller: Aufruf einer Methode vom Rückgabetyp String, auch Parameter nutzbar (dann Klammern)<h:commandButton value="Abschicken"
action="#{modul.uebernehmen}"/>
• Im Modell:public String uebernehmen(){
return "/ausgabe.xhtml"; // Endung weglassbar
}
• Methode kann natürlich abhängig von Variablen unterschiedliche Seiten liefern
• Beachten: Navigation kann in den Tiefen des Codes verschwinden ( -> Konstanten nutzen, Architektur)
• gibt XML-basierte Variante
• Mit public void-Methoden wird auf gleicher Seite geblieben
Prof. Dr. Stephan Kleuker
239Komponentenbasierte Software-Entwicklung
Lebensdauer von Informationen (Scope)
• Anmerkung: Obwohl es verlockend ist, viele Informationen in Session oder Application zu legen, ist dies wegen Performance verboten (wird gespeichert, evtl. passiviert, hat wilde Referenzen, Zugriff muss ggfls. synchronisiert werden)
• fehlt: CoversationScoped; im Programm Scope starten und enden
Request
Session
Application
für eineNutzer-Sitzung
solangeaktuellesDeploymentläuft
Zeitnur ein Aufruf
Prof. Dr. Stephan Kleuker
240
Scope-Beispiel (1/5) - Bean-Klassen
//@Named("modul")
//@RequestScoped Modul als einfache Klasse (wichtig!)
public class Modul implements Serializable { ...// wie vorher
@Named(value = "modulr")
@RequestScoped
public class ModulRequest extends Modul{}
@Named("moduls")
@SessionScoped
public class ModulSession extends Modul{}
@Named("modula")
@ApplicationScoped
public class ModulApplication extends Modul{}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
241Komponentenbasierte Software-Entwicklung
Scope-Beispiel (2/5) - Ausschnitt index.xhtml
<h:form>
<h:panelGrid columns="3" >
<h:outputLabel for="mnamer" value="Modulname "/>
<h:inputText id="mnamer" value="#{modulr.name}"/>
<h:message for="mnamer" />
<h:outputLabel for="mnrr" value="Modulnummer "/>
<h:inputText id="mnrr" value="#{modulr.nr}"/>
<h:message for="mnrr" />
<!-- auch moduls und modula -->
<h:commandButton value="Abschicken"
action="#{modulr.uebernehmen}"/>
</h:panelGrid>
</h:form>
Prof. Dr. Stephan Kleuker
242Komponentenbasierte Software-Entwicklung
Scope-Beispiel (3/5) - Ausschnitt ausgabe.xhtml
<h:form>
<h:messages globalOnly="true"/>
<h:outputText value="Modulname r: "/>
<h:outputText id="mnamer" value="#{modulr.name}"/> <br/>
<h:outputText value="Modulnummer r: "/>
<h:outputText id="mnrr" value="#{modulr.nr}"/><br/>
<h:commandButton value="Zur Eingabe"
action="#{modulr.eingeben}"/><br/>
<!-- auch moduls und modula -->
<h:commandButton value="Ausgabe wiederholen"
action="#{modulr.uebernehmen}"/>
</h:form>
Prof. Dr. Stephan Kleuker
243Komponentenbasierte Software-Entwicklung
Scope-Beispiel (4/5) - Ein Nutzer, zwei Klicks
Prof. Dr. Stephan Kleuker
244Komponentenbasierte Software-Entwicklung
Scope-Beispiel (5/5) - Zwei NutzerN
utz
er
1N
utz
er
2
Zeit
Prof. Dr. Stephan Kleuker
245
ConversationScope (1/4)
Komponentenbasierte Software-Entwicklung
• Entwickler kann selbst Länge der Session bestimmen, damit zwischen RequestScope und SessionScope
import javax.enterprise.context.Conversation;
import javax.enterprise.context.ConversationScoped;
import javax.inject.Inject;
import javax.inject.Named;
@ConversationScoped
@Named("scope")
public class Scopeanalyse implements Serializable {
private String wert;
private List<String> liste = new ArrayList<>();
@Inject
private Conversation conver;
Vorgriff auf CDI:Container stellt
Conversation-Objekt zur Verfügung
Prof. Dr. Stephan Kleuker
246
ConversationScope (2/4)
Komponentenbasierte Software-Entwicklung
public Scopeanalyse(){} // get- und set-Methoden fehlen
public void hinzu(){
if(this.conver.isTransient()){
this.conver.begin();
}
if(this.wert != null && !this.wert.trim().equals("")){
this.liste.add(wert);
}
} // kein String zurueck, da auf gleicher Seite geblieben
public void vergiss(){
if (!this.conver.isTransient()){
this.conver.end();
}
}
Prof. Dr. Stephan Kleuker
247
ConversationScope (3/4)
<h:head>
<title>Scope Spielerei</title>
</h:head>
<h:body>
<h:form id="main">
<h:inputTextarea id="ein" value="#{scope.wert}" rows="3"/><br/>
<ui:repeat value="#{scope.liste}" var="w">
<h:outputText value="#{w} "/>
</ui:repeat> <br/>
<h:commandButton value="Übernehmen" action="#{scope.hinzu}"/>
<h:commandButton value="Vergessen" action="#{scope.vergiss}"/>
</h:form>
</h:body>
Komponentenbasierte Software-Entwicklung
Anmerkung: mit <ui:repeat> wird über eine Sammlung (scope.liste) iteriert, jeweiliges Objekt in Laufvariable w
Prof. Dr. Stephan Kleuker
248
ConversationScope (4/4)
• Bild jeweils nach dem Klicken
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
249
Ausgabeprobleme: Umlaute und Zeilenumbrüche
• vor Knopfnutzung danach
• danach
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
250
Umlaute – Text-Encoding
• Sonderzeichen, wie Umlaute bei Eingaben problematisch
• wenn konsequent UTF-8 genutzt, dann kein Problem; trifft man öfter nicht an
• häufig, gerade im MS-Bereich, nur ISO-8859-1 nutzbar
• nicht ganz sauberer Trick: Eingaben selbst passend umwandeln (läuft ggfls. nicht in Ländern mit nichtlateinischer Schrift)
String s1 = new String("äöüÄÖßÜ");
System.out.println(s1);
String s2 = new String(s1.getBytes("UTF-8"), "ISO-8859-1");
System.out.println(s2);
String s3 = new String(s2.getBytes("UTF-8"), "ISO-8859-1");
System.out.println(s3);
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
251
Umlaute – saubere Glassfish-Lösung
• Codierung des Glassfish auf UTF-8 umstellen in glassfish-web.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD
GlassFish Application Server 3.1 Servlet 3.0//EN"
"http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
<glassfish-web-app error-url="">
<class-loader delegate="true"/>
<locale-charset-info default-locale="">
<locale-charset-map locale="" charset=""/>
<parameter-encoding default-charset="UTF-8"/>
</locale-charset-info>
</glassfish-web-app>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
252
Zeilenumbrüche (1/2)
• verschiedene Lösungen, alle nicht optimal
• Konvertierer schreiben <h:outputText … converter="wandel"
• hier: HTML-Wissen nutzen, genauer CSS
<h:inputTextarea id="ein" value="#{scope.wert}" rows="3"/><br/>
<div style="white-space: pre-wrap">
<ui:repeat value="#{scope.liste}" var="w">
<h:outputText value="#{w} "/>
</ui:repeat> </div>
<h:commandButton value="Übernehmen" action="#{scope.hinzu}"/>
<h:commandButton value="Vergessen" action="#{scope.vergiss}"/>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
253
Zeilenumbrüche (2/2)
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
254
Einschub: IE 9 – ggfls. Problem
• Programm läuft problemlos in Firefox und Chrome
• in IE 8 gab es auch keine Probleme
• in IE 9, erfolgt nach Drücken des Knopfes folgende Ausgabe
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
255
Einschub: IE 9 - Lösung
• Session-Informationen können statt im Server auch im Client gehalten werden
• entlastet Server, ist aber für Nutzer langsamer
• Ergänzung in web.xml:
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
• Anmerkung: fehlt systematische Fehleranalyse: NetBeans 6.3.1, Glassfish 4, MS IE 9, MS IE 9 Nutzereinstellungen, Win7 x64, …
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
256
Kein Konstruktor in Managed Beans (Scopes)
• generell vieles nebenläufig
• unklar, wann Objektvariablen und andere Objekte genau erzeugt werden
• Races möglich: Zugriff auf Objekt, das gerade Konstruktor durchläuft
• alle Scopes können Methoden mit folgenden Annotationen nutzen
• @PostConstruct: nachdem das Objekt erzeugt wurde; sinnvoll hier Variablen initialisieren; Nutzung statt Konstruktor
• @PreDestroy: bevor Scope abläuft (aufräumen); auch wenn Session beendet wird
• Anmerkung: für kritische Ressourcen sinnvoll Ergänzung zu @PreDestroy zum Aufräumen
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
257
Nutzung @PostConstruct und @PreDestroy (1/5)@SessionScoped
public class Session implements Serializable{
private int zaehler;
public Session(){
System.out.println("Session Konstruktor: " + new Date());
}
@PostConstruct
public void init(){
System.out.println("Session PostConstruct: " + new Date());
}
@PreDestroy
public void destroy(){
System.out.println("Session PreDestroy: " + new Date());
}
public int plus(){
return ++this.zaehler;
}
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
258
Nutzung @PostConstruct und @PreDestroy (2/5)@RequestScoped
@Named
public class Bean implements Serializable{
@Inject
private Session session;
public Bean(){
System.out.println("Bean Konstruktor: " + new Date()); }
@PostConstruct
public void init(){
System.out.println("Bean PostConstruct: " + new Date());}
@PreDestroy
public void destroy(){
System.out.println("Bean PreDestroy: " + new Date());}
public void mach(){ // ohne Rückgabe, dann auf Seite bleiben
System.out.println("mach: " + this.session.plus());
}
} Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
259
Nutzung @PostConstruct und @PreDestroy (3/5)
• index.xhtml<h:body>
<h:form id="form">
<h:commandButton id="b" value="mach"
action="#{bean.mach}"/>
</h:form>
</h:body>
• Sessiondauer in web.xml<session-config>
<session-timeout>
1
</session-timeout>
</session-config>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
260
Nutzung @PostConstruct und @PreDestroy (4/5)Information: Bean Konstruktor: Mon Oct 30 16:15:53 CET 2017
Information: Session Konstruktor: Mon Oct 30 16:15:53 CET 2017
Information: Bean PostConstruct: Mon Oct 30 16:15:53 CET 2017
Information: Session Konstruktor: Mon Oct 30 16:15:53 CET 2017
Information: Session PostConstruct: Mon Oct 30 16:15:53 CET 2017
Information: mach: 1
Information: Bean PreDestroy: Mon Oct 30 16:15:53 CET 2017
Information: Bean Konstruktor: Mon Oct 30 16:16:16 CET 2017
Information: Bean PostConstruct: Mon Oct 30 16:16:16 CET 2017
Information: mach: 2
Information: Bean PreDestroy: Mon Oct 30 16:16:16 CET 2017
Information: Bean Konstruktor: Mon Oct 30 16:16:26 CET 2017
Information: Bean PostConstruct: Mon Oct 30 16:16:26 CET 2017
Information: mach: 3
Information: Bean PreDestroy: Mon Oct 30 16:16:26 CET 2017
Information: Session PreDestroy: Mon Oct 30 16:18:42 CET 2017
FATAL: JSF1073: javax.faces.application.ViewExpiredException
FATAL: viewId:/index.xhtml - Ansicht /index.xhtml konnte nicht
wiederhergestellt werden.
Komponentenbasierte Software-Entwicklung
nach Klick (oder längerem Warten)
Prof. Dr. Stephan Kleuker
261
Nutzung @PostConstruct und @PreDestroy (5/5)
• kleine Änderung in Bean-Klassepublic Bean(){
System.out.println("mach: " + this.session.plus());
System.out.println("Bean Konstruktor: " + new Date());
}
• möglicher Effekt bei erstem Klick:Warnung: #{bean.mach}: java.lang.NullPointerException
javax.faces.FacesException: #{bean.mach}:
java.lang.NullPointerException
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
262Komponentenbasierte Software-Entwicklung
Erinnerung: Konstanten in Programmen
• schlecht return "ANZEIGEN";
• besser: Konstanten getrennt deklarierenpublic class Modul implements Serializable{
private final static string ANZEIGEN = "ANZEIGEN";
...
return ANZEIGEN;
• Alternativ: Klasse mit Konstantenpackage konstanten;
public class Navigation{
public final static string ANZEIGEN = "ANZEIGEN";
...
return konstanten.Navigation.ANZEIGEN;
– Entwicklungsumgebungen erlauben dann einfache Suche und Änderung (Refactoring)
Prof. Dr. Stephan Kleuker
263Komponentenbasierte Software-Entwicklung
Standardattribut rendered (1/3)
• JSF-Elemente werden typischerweise ineinander geschachtelt (typische GUI-Idee der Schächtelchen in Schächtelchen)
• viele (Schachtel-)Elemente haben Attribut rendered; Boolescher Wert, ob Element dargestellt werden soll
@Named("mo")
@SessionScoped
public class Mojo implements Serializable {
private boolean jo=true;
public Mojo(){}
public boolean getJo() {return jo;}
public void setJo(boolean jo) {this.jo = jo;}
public String change(){
jo=!jo;
return "/index.xhtml";
}
}
Prof. Dr. Stephan Kleuker
264
<?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>Klickklack</title> </h:head>
<h:body>
<h:form id="f1">
<h:panelGrid id="p1" rendered="#{mo.jo}">
<h:outputLabel value=„HS rocks"/>
</h:panelGrid>
<h:panelGrid id="p2" rendered="#{!mo.jo}">
<h:outputLabel value="OS rocks"/>
</h:panelGrid>
<h:commandButton action="#{mo.change}" value="Press"/>
</h:form>
</h:body>
</html>
Standardattribut rendered (2/3) - JSF-Seite
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
265Komponentenbasierte Software-Entwicklung
Standardattribut rendered (3/3)
Prof. Dr. Stephan Kleuker
266Komponentenbasierte Software-Entwicklung
Strukturierung mit panelgrid / panelgroup
• mit h:panelgrid können einzelne Komponenten zu einem Block zusammengefasst und einheitlich behandelt werden (ähnlich zu JPanel in Swing)
• durch columns="3" eine Spaltenanzahl angebbar• Ausgabe erfolgt in Tabellenform• Elemente werden zeilenweise ergänzt
• mit h:panelgroup Sammlung ohne Formatierung möglich, auch als Block zusammengefasst, z. B.:<h:panelGrid rendered="#{! empty controller.foren}">
<ui:repeat value="#{controller.foren}" var="f" >
…
</ui:repeat>
</h:panelGrid>
Prof. Dr. Stephan Kleuker
267Komponentenbasierte Software-Entwicklung
Kommentare
• (?!) Kommentare In JSF-Seiten <!-- Dies liest eh keiner -->
wandern in Ausgabe, Ausdrücke mit #{…} werden ausgewertet
• Ergänzung in web.xml:
<context-param>
<param-name>javax.faces.FACELETS_SKIP_COMMENTS</param-name>
<param-value>true</param-value>
</context-param>
Prof. Dr. Stephan Kleuker
268
Unified Expression Language
• Zugriff auf Exemplarvariablen und Methoden der ManagedBeans
• zentrale Typen: Zahlen und Strings (können sehr gut verarbeitet und verglichen werden)
• Schreiben von Werten über set-Methoden "#{mo.jo}"
• Lesen erfolgt über get-Methoden, generell Berechnungen möglich, die beim Schreiben keinen Sinn haben "#{!mo.jo}"
• Bei Methoden mit fest vorgegebener Aufgabe (vorgegebener Signatur), stehen keine Klammern beim Aufruf action="#{mo.change}" sonst ja "#{bean1.machWasMit(bean2)}"
• Zugriff auf Variablen aus festen Namensraum: ManagedBeans und Servlet Parameter
• JSR 341: Expression Language 3.0, https://jcp.org/en/jsr/detail?id=341
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
269
Implizite Objekte der EL (Ausschnitt)
• requestScope (analog sessionScope, applicationScope)Zugriff auf Request-Map des External-Contexts
• param
Zugriff auf Request-Parameter-Map des External-Contexts
• header
Zugriff auf Request-Header-Map des External-Contexts
• facesContext
Zugriff auf den Faces-Context
• initParam
Zugriff auf Kontextparameter der Webapplikation
• cookie
Zugriff auf die Request-Cookie-Map des External-Contexts
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
270
Zusammenspiel von Scopes (1/4)
• Beispiel: Gemeinsame Erstellung eines Einkaufszettels, jeder kann Zeilen hinzufügen (nur temporärer Speicher im ApplicationScope; Eingabe benötigt nur RequestScope)
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
271
Zusammenspiel von Scopes (2/4)
@ApplicationScoped
public class Gedicht implements Serializable{
private List<String> zeilen;
@PostConstruct
public void init() {
this.zeilen = new ArrayList<>();
}
public void zeileHinzu(String text) {
this.zeilen.add(text);
}
public List<String> getZeilen() {
return this.zeilen;
}
public void setZeilen(List<String> zeilen) {
this.zeilen = zeilen;
}
}Komponentenbasierte Software-
Entwicklung
Prof. Dr. Stephan Kleuker
272
Zusammenspiel von Scopes (3/4)@Named("vc")
@RequestScoped
public class ViewController {
@Inject
private Gedicht gedicht;
private String eingabe;
public void hinzu(){
this.gedicht.zeileHinzu(eingabe);
this.eingabe = "";
}
public List<String> getText() {
return this.gedicht.getZeilen();
}
public String getEingabe() { return this.eingabe; }
public void setEingabe(String eingabe) {
this.eingabe = eingabe;
}
}
Komponentenbasierte Software-Entwicklung
kleiner Trick: get-Methode ohne
Exemplarvariable
void-Methode bleibt auf gleicher
Seite
Prof. Dr. Stephan Kleuker
273
Zusammenspiel von Scopes (4/4)
<h:head>
<title>WG-Einkaufszettel</title>
</h:head>
<h:body>
<h:form id="main">
Ihre Eingabe: <h:inputText id="eingabe" size="30"
value="#{vc.eingabe}"/>
<h:commandButton id ="hinzu" value="Hinzu"
action="#{vc.hinzu}"/>
<br/>
<ui:repeat var="z" value="#{vc.text}">
#{z} <br/>
</ui:repeat>
</h:form>
</h:body>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
274Komponentenbasierte Software-Entwicklung
4.3 Validierung/ Fehleingaben im nullten Beispiel (1/2)
Eingabe:
Fehler wird automatisch unter der bleibender Seite ausgegebenj_idt7 ist interner Name, mname ist „unsere“ id des Eingabefeldes
Prof. Dr. Stephan Kleuker
275Komponentenbasierte Software-Entwicklung
Fehleingaben im nullten Beispiel (2/2)
Anpassung des Projektstatus (oder Parameter löschen) in web.xml
(Development, UnitTest, SystemTest, Production)<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<!-- <param-value>Development</param-value> -->
<param-value>Production</param-value>
</context-param>
Applikation bleibt auf der Seite, keine sichtbare Fehlermeldungim Server-Log:
INFO: WARNUNG: FacesMessage(s) wurde(n) in die Warteschlange
gestellt, aber möglicherweise nicht angezeigt.
sourceId=j_idt7:mname[severity=(ERROR 2), summary=(j_idt7:mname:
Validierungs-Fehler: Wert wird benötigt.),
detail=(j_idt7:mname: Validierungs-Fehler: Wert wird
benötigt.)]
Prof. Dr. Stephan Kleuker
276
Direkte Validierungen - einige Möglichkeiten<h:form>
<h:panelGrid columns="3" >
<h:outputLabel for="mname" value="Modulname "/>
<h:inputText id="mname" value="#{modul.name}"
required="true" size="8" requiredMessage="nich leer"
validatorMessage="4 Zeichen">
<f:validateLength minimum="4" maximum="4"/>
</h:inputText>
<h:message for="mname" />
<h:outputLabel for="mnr" value="Modulnummer "/>
<h:inputText id="mnr" value="#{modul.nr}" required="true"
requiredMessage="Eingabe notwendig" size="4"
converterMessage="normale Zahl"/>
<h:message for="mnr" />
<h:commandButton value="Abschicken"
action="#{modul.uebernehmen}"/>
</h:panelGrid>
</h:form>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
277Komponentenbasierte Software-Entwicklung
Ausgaben beim Drücken von „Abschicken“
Prof. Dr. Stephan Kleuker
278
Globale Fehlermeldungen erzeugen und anzeigen
public String uebernehmen() {
if (name.equals("OOAD")) {
FacesContext ctxt = FacesContext.getCurrentInstance();
FacesMessage ms = new FacesMessage();
ms.setSeverity(FacesMessage.SEVERITY_ERROR);
ms.setSummary("OOAD ist schon da");
ms.setDetail("OOAD im Standard");
ctxt.addMessage(null, ms);
}
return "ANZEIGEN";
}
//in ausgabe.xhtml
<h:form>
<h:messages globalOnly="true"/>
<h:outputText value="Modulname: "/>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
279Komponentenbasierte Software-Entwicklung
Validierer selbst gestrickt (1/2)
public class Modul implements Serializable {
...
// Validierungsmethoden können beliebigen Namen haben,
// müssen aber die folgende Signatur haben
public void pruefe(FacesContext context,
UIComponent component,
Object value)
throws ValidatorException {
if (((String) value).equals("OOAD")) {
throw new ValidatorException(new FacesMessage(
FacesMessage.SEVERITY_ERROR, "oweh", "ole ole"));
}
}
...
Hinweis: wird „?faces-redirect=true“ genutzt, ist zu ergänzen:context.getExternalContext().getFlash().setKeepMessages(true);
Prof. Dr. Stephan Kleuker
280Komponentenbasierte Software-Entwicklung
Validierer selbst gestrickt (2/2)
• in index.xhtml<h:panelGrid columns="3" >
<h:outputLabel for="mname" value="Modulname "/>
<h:inputText id="mname" value="#{modul.name}"
validator="#{modul.pruefe}"/>
<h:message for="mname" />
Prof. Dr. Stephan Kleuker
281
Anmerkungen zu vorherigen Folien
• man kann eigene Validierungsmethoden schreiben
• man könnte auch hier BeanValidation nutzen (folgt später), muss dies aber selbst zu passenden Ausgaben umwandeln
• FacesContext ist der zentrale Zugang zu den Innereien von JSF
• Zugriff bis auf genutzte Servlets mit deren Parametern möglich
• leider auch eine Art Voodoo-Klasse, da fast die gesamte JSF-Steuerung manipulierbar und so keine JSF-Schicht mehr richtig existiert
• FacesContext macht Code-Wiederverwendung mit anderen Technologien fast unmöglich; deshalb hier nicht intensiv betrachtet
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
282Komponentenbasierte Software-Entwicklung
4.4 JSF-Lebenszyklus
JSF-Klassen: typischer GUI-Aufbau mit Events• Hierarchische Baumstruktur der Komponenten, z. B.
– Intern: Wurzel des Komponentenbaums, Container– <h:form> Formular, benötigt zum Daten verschicken– <h:panelGrid> Container mit Tabellendarstellung
• public abstract class UIComponent extends Object implements
StateHolder: UIComponent is the base class for all user interface components in JavaServer Faces. The set of UIComponent instances associated with a particular request and response are organized into a component tree under a UIViewRoot that represents the entire content of the request or response. (Oracle Klassendoku)
• public abstract class UIComponentBase extends UIComponent
• public class UICommand extends UIComponentBase implements
ActionSource2
• public class UIOutput extends UIComponentBase implements
ValueHolder
Prof. Dr. Stephan Kleuker
283Komponentenbasierte Software-Entwicklung
Analyse von FacesContext (1/4)
<h:form id="f1">
<h:messages id="m1" globalOnly="true"/>
<h:panelGrid id="p1" columns="3" >
<h:outputLabel id="o1" for="mname" value="Modulname "/>
<h:inputText id="mname" value="#{module.modul.name}"/>
<h:message id="m2" for="mname" />
<h:outputLabel id="o2" for="mnr" value="Modulnummer "/>
<h:inputText id="mnr" value="#{module.modul.nr}"/>
<h:message id="m3" for="mnr"/>
<h:commandButton id="c1" value="Abschicken"
action="#{module.uebernehmen}"/>
</h:panelGrid>
<h:commandLink id="anz" action="#{module.anzeigen}" >
<h:outputText value="Zur Modulübersicht"/>
</h:commandLink><br/>
<h:commandLink id="intern"
action="#{module.intern}" >
<h:outputText value="JSFAufbauinfos"/>
</h:commandLink>
</h:form>
Prof. Dr. Stephan Kleuker
284Komponentenbasierte Software-Entwicklung
Analyse von FacesContext (2/4)
private void baum(UIComponent ui, String leer) {
System.out.println(leer + ui.getId() + ": "
+ ui.getRendererType() +" : "
+ ui.getClass());
List<UIComponent> com = ui.getChildren();
for (UIComponent u : com) {
baum(u, leer + " ");
}
}
public String intern() {
FacesContext ctxt = FacesContext.getCurrentInstance();
UIViewRoot vr = ctxt.getViewRoot();
if (vr != null) {
System.out.println("Root:" + vr.getViewId());
baum(vr, " ");
} else
System.out.println("keine Root");
return ANZEIGEN;
}
Prof. Dr. Stephan Kleuker
285Komponentenbasierte Software-Entwicklung
Analyse von FacesContext (3/4)
INFO: Root:/index.xhtmlINFO: j_id1: null : class javax.faces.component.UIViewRootINFO: j_idt2: null : class com.sun.faces.facelets.compiler.UIInstructionsINFO: j_idt3: null : class com.sun.faces.facelets.compiler.UIInstructionsINFO: j_idt4: javax.faces.Head : class javax.faces.component.UIOutputINFO: j_idt5: null : class com.sun.faces.facelets.compiler.UIInstructionsINFO: j_idt6: javax.faces.Body : class javax.faces.component.UIOutputINFO: f1: javax.faces.Form : class javax.faces.component.html.HtmlFormINFO: m1: javax.faces.Messages : class javax.faces.component.html.HtmlMessagesINFO: p1: javax.faces.Grid : class javax.faces.component.html.HtmlPanelGridINFO: o1: javax.faces.Label : class javax.faces.component.html.HtmlOutputLabelINFO: mname: javax.faces.Text : class javax.faces.component.html.HtmlInputTextINFO: m2: javax.faces.Message : class javax.faces.component.html.HtmlMessageINFO: o2: javax.faces.Label : class javax.faces.component.html.HtmlOutputLabelINFO: mnr: javax.faces.Text : class javax.faces.component.html.HtmlInputTextINFO: m3: javax.faces.Message : class javax.faces.component.html.HtmlMessageINFO: c1: javax.faces.Button : class javax.faces.component.html.HtmlCommandButtonINFO: anz: javax.faces.Link : class javax.faces.component.html.HtmlCommandLinkINFO: j_idt15: javax.faces.Text : class javax.faces.component.html.HtmlOutputTextINFO: j_idt16: null : class com.sun.faces.facelets.compiler.UIInstructionsINFO: intern: javax.faces.Link : class javax.faces.component.html.HtmlCommandLinkINFO: j_idt17: javax.faces.Text : class javax.faces.component.html.HtmlOutputTextINFO: j_idt18: null : class com.sun.faces.facelets.compiler.UIInstructionsGrundregel: Layout bleibt statisch; Ein- und Ausblenden durch Attribut rendered
Prof. Dr. Stephan Kleuker
286Komponentenbasierte Software-Entwicklung
Analyse von FacesContext (4/4) - KomponentenbaumUIViewRoot
HtmlMessages
HtmlForm
HtmlPanelGrid
HtmlOutputLabel
HtmlInputText
HtmlMessage
HtmlOutputLabel
HtmlInputText
HtmlMessage
HtmlCommandButton
HtmlCommandLink
HtmlOutputText
HtmlCommandLink
HtmlOutputText
Prof. Dr. Stephan Kleuker
287Komponentenbasierte Software-Entwicklung
Speichern der Zustandsinformationen (1/2)
• Klassische GUI-Komponenten (und Event-Listener) sind zustandsbasiert; HTTP nicht
• Zustand muss gesichert werden (wie wird in DeploymentDescriptor beschrieben)
– Variante A: auf dem Server (Zuordnung z. B. über Cookies)
• empfohlen; nicht default bei Netbeans
• hohe Speicheranforderung
– Variante B: beim Client<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
• anzuzeigende Seite enthält Zustand in hidden Field
Prof. Dr. Stephan Kleuker
288Komponentenbasierte Software-Entwicklung
Speichern der Zustandsinformationen (2/2)
Seitenende in generiertem HTML (Ausschnitt):<a id="f1:intern" href="#" onclick="if(typeof jsfcljs ==
'function'){jsfcljs(document.forms['f1'],'f1:intern,
f1:intern','');}return false">JSFAufbauinfos</a>
<input type="hidden" name="javax.faces.ViewState"
id="javax.faces.ViewState"
value="H4sIAAAAAAAAANVXW2wUVRg+O9s7FXrhGlNaAiIYu2W7i0HQSIF
eFndb0i1F4KGcnT3bnTI3Z850BogEHtREEqNBEk0wavDBB3hRn4jxEh6M
JJhI4ouJCTEmxsRLYkxQH9Tzn9mZnd2dbSliopNmembm/875/tt3zl7+E
VaBuo+mp7D8zgmY3U2NpGbIyLd9dLnT73RYW6VBYQcHSG0wjRQQtSUmGm
sQIWiRnDui5LIqaSpsayFFOSwSqeJUZK0eVNUwYh41qe/Fq4+uGVbaMft
cM8dhzB1QerOaVZ2Jy6phKVxg6mpiViT2oaRa1zM1Ke/cWTjvk0Oo0Eu5
crjvbjgkk44FU8Al+KOW8oi1eicXBItKbBZsFjNYz+6pNrq499EU=" />
</form>
</body>
</html>
Prof. Dr. Stephan Kleuker
289Komponentenbasierte Software-Entwicklung
Hintergrund: Abarbeitung von JSF-Anfragen
Prof. Dr. Stephan Kleuker
290Komponentenbasierte Software-Entwicklung
Restore View
Komponentenbaum (wieder-)herstellen
• basiert auf nicht sichtbarer Wurzelkomponente
• Komponentenbaum in FacesContext gespeichert
• erster Aufruf:
– JSF-Seite rendern, nicht anzeigen
– ID-Vergabe und damit Aufbau der internen Struktur
– Registrieren von Event-Listenern und Validatoren
– Werte in Komponenten eintragen
• sonst: zugehörigen Komponentenbaum suchen, bzw. wieder aufbauen (beinhaltet auch Eventlistener, Konvertierer und Backing-Beans)
Prof. Dr. Stephan Kleuker
291Komponentenbasierte Software-Entwicklung
Apply Request Values
UI-Komponenten mit Anfrageparametern aktualisieren
• Request-Parameter auslesen (einschließlich JSF 1.2 nur POST)
• POST-String parsen und filtern
• Werte dann UI-Komponenten zuordnen (noch nicht prüfen)
• nutzt Methode processDecodes() für alle Komponenten
• falls Attribut immediate=„true“, dann (gleich genauer)
– für Aktionselemente (Command): konvertieren und validieren überspringen
– für Eingabeelemente (Input): sofort konvertieren und validieren
Prof. Dr. Stephan Kleuker
292Komponentenbasierte Software-Entwicklung
Process Validations
Werte konvertieren und validieren
• Jede UI-Komponente validiert und konvertiert den zugeordneten Wert
• nutzt Methode processValidators() der Komponenten
• automatische Konvertierung für viele Typen; Konvertiererselbst erstellbar (ähnlich zu Validierer)
• bei Fehler Fehlermeldung in Fehlerliste eintragen und Modellaktualisierung überspringen; zur Ausgabe
• bei Wertänderung: ValueChangeEvent
• Anmerkung: Werte noch nicht in Beans
Prof. Dr. Stephan Kleuker
293Komponentenbasierte Software-Entwicklung
Update Model Values
Werte im Modell (Beans) aktualisieren
• Typsicherheit durch vorherige Schritte garantiert
• nutzt Methode processUpdates() der Komponenten
• Werte aktualisieren, durch set-Methoden
• Abbruch bei Fehler
Prof. Dr. Stephan Kleuker
294Komponentenbasierte Software-Entwicklung
Invoke Applications
Anwendung ausführen
• an das Ereignis gebundene Aktion ausführen, z. B. Action-Methode
• nutzt Methode processApplication() der Komponenten
• arbeitet mit vorher aktualisierten Werten
Prof. Dr. Stephan Kleuker
295Komponentenbasierte Software-Entwicklung
Render Response
• Rendern durch Spezifikation nicht festgelegt
• Antwort generieren
• aktuellen Stand des zugehörigen Komponentenbaums ausgeben
• rekursiver Baumdurchlauf; jeweils Renderer aufrufen (HTML, XTHML, XUL, ...)
• jede Komponente hat Methode der Form encodeXXX()
• abspeichern des Komponentenbaums
• generierte Seite an Server übergeben, der sie typischerweise an den Browser schickt
Prof. Dr. Stephan Kleuker
296Komponentenbasierte Software-Entwicklung
JSF-Lifecycle verfolgen (1/8)
• Man kann PhaseListener schreiben, die über Phasenänderungen informiert werden
• wird in faces-config.xml im Ordner WEB-INF festgehalten (neue JSF Faces Configuration)
<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="2.2"
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_2.xsd">
<lifecycle>
<phase-listener>controller.ZeichPhasen</phase-listener>
</lifecycle>
</faces-config>
Prof. Dr. Stephan Kleuker
297Komponentenbasierte Software-Entwicklung
JSF-Lifecycle verfolgen (2/8) - index.xhtml
<h:form id="form1">
<h:panelGrid id="panel1" columns="3" rules="all">
<h:outputText id="o1" value="neuer Text"/>
<h:inputText id="i1" value="#{analyse.ein}"
required="true"/>
<h:message id="m1" for="i1"/>
<h:outputText id="ow1" value="neue Zahl"/>
<h:inputText id="iw1" value="#{analyse.zahl}"/>
<h:message id="m1w" for="iw1"/>
<h:outputText id="o2" value="alter Text/Zahl"/>
<h:outputText id="o3" escape="false"
value="#{analyse.ein}"/>
<h:outputLabel id="ow3" value="#{analyse.zahl}"/>
<h:commandButton id="c1" value="Übernehmen"
action="#{analyse.uebernehmen}"/>
</h:panelGrid>
</h:form>
Prof. Dr. Stephan Kleuker
298Komponentenbasierte Software-Entwicklung
JSF-Lifecycle verfolgen (3/8) - Bean
@Named
@SessionScoped
public class Analyse implements Serializable{
private String ein;
private int zahl;
public Analyse(){}
public String uebernehmen(){
return "/index.xhtml";
}
public String getEin() {return ein;}
public void setEin(String ein) {this.ein = ein;}
public int getZahl() {return zahl;}
public void setZahl(int zahl) {this.zahl = zahl;}
}
Prof. Dr. Stephan Kleuker
299Komponentenbasierte Software-Entwicklung
JSF-Lifecycle verfolgen (4/8) - PhaseListener [1/2]
public class ZeichPhasen implements PhaseListener {
// Hier angeben, in welchen Phasen dieser Listener genutzt
// werden soll, im Beispiel in allen
@Override
public PhaseId getPhaseId() {
return PhaseId.ANY_PHASE;
}
@Override
public void beforePhase(PhaseEvent e) {
if (e.getPhaseId() == PhaseId.RESTORE_VIEW)
System.out.println("===geht los");
System.out.println("before: " + e.getPhaseId());
zeichAnalyse();
}
Prof. Dr. Stephan Kleuker
300Komponentenbasierte Software-Entwicklung
JSF-Lifecycle verfolgen (5/8) - PhaseListener [2/2]
@Override
public void afterPhase(PhaseEvent e) {
System.out.println("after: " + e.getPhaseId());
zeichAnalyse();
if (e.getPhaseId() == PhaseId.RENDER_RESPONSE)
System.out.println("===is Schicht");
}
private void zeichAnalyse() {
FacesContext fc = FacesContext.getCurrentInstance();
Analyse a = fc.getApplication()
.evaluateExpressionGet(fc, "#{analyse}",
Analyse.class);
System.out.println("A: "+a.getEin() +" :: "+ a.getZahl());
}
}
Prof. Dr. Stephan Kleuker
301Komponentenbasierte Software-Entwicklung
JSF-Lifecycle verfolgen (6/8) - Applikationsstart
INFO: ===geht los
INFO: before: RESTORE_VIEW 1
INFO: A: null :: 0
INFO: after: RESTORE_VIEW 1
INFO: A: null :: 0
INFO: before: RENDER_RESPONSE 6
INFO: A: null :: 0
INFO: after: RENDER_RESPONSE 6
INFO: A: null :: 0
INFO: ===is Schicht
Nutzer macht Eingaben
Prof. Dr. Stephan Kleuker
302Komponentenbasierte Software-Entwicklung
JSF-Lifecycle verfolgen (7/8) - erfolgreiche ÜbernahmeINFO: ===geht los
INFO: before: RESTORE_VIEW 1
INFO: A: null :: 0
INFO: after: RESTORE_VIEW 1
INFO: A: null :: 0
INFO: before: APPLY_REQUEST_VALUES 2
INFO: A: null :: 0
INFO: after: APPLY_REQUEST_VALUES 2
INFO: A: null :: 0
INFO: before: PROCESS_VALIDATIONS 3
INFO: A: null :: 0
INFO: after: PROCESS_VALIDATIONS 3
INFO: A: null :: 0
INFO: before: UPDATE_MODEL_VALUES 4
INFO: A: null :: 0
INFO: after: UPDATE_MODEL_VALUES 4
INFO: A: Zaphod :: 42
INFO: before: INVOKE_APPLICATION 5
INFO: A: Zaphod :: 42
INFO: after: INVOKE_APPLICATION 5
INFO: A: Zaphod :: 42
INFO: before: RENDER_RESPONSE 6
INFO: A: Zaphod :: 42
INFO: after: RENDER_RESPONSE 6
INFO: A: Zaphod :: 42
INFO: ===is Schicht
Prof. Dr. Stephan Kleuker
303Komponentenbasierte Software-Entwicklung
JSF-Lifecycle verfolgen (8/8) - falsche EingabeINFO: ===geht los
INFO: before: RESTORE_VIEW 1
INFO: A: Zaphod :: 42
INFO: after: RESTORE_VIEW 1
INFO: A: Zaphod :: 42
INFO: before: APPLY_REQUEST_VALUES 2
INFO: A: Zaphod :: 42
INFO: after: APPLY_REQUEST_VALUES 2
INFO: A: Zaphod :: 42
INFO: before: PROCESS_VALIDATIONS 3
INFO: A: Zaphod :: 42
INFO: after: PROCESS_VALIDATIONS 3
INFO: A: Zaphod :: 42
INFO: before: RENDER_RESPONSE 6
INFO: A: Zaphod :: 42
INFO: after: RENDER_RESPONSE 6
INFO: A: Zaphod :: 42
INFO: ===is Schicht
Prof. Dr. Stephan Kleuker
304Komponentenbasierte Software-Entwicklung
Problem: Abbrüche (1/2)in Analyse :
public String zuruecksetzen(){
ein="";
zahl=0;
return "/index.xhtml";
}
• in index.xhtml<h:commandButton id="c2" value="Zurücksetzen"
action="#{analyse.zuruecksetzen}"/>
• Problem: Validierung läuft trotzdem
Prof. Dr. Stephan Kleuker
305Komponentenbasierte Software-Entwicklung
Problem: Abbrüche (2/2)
• immediate=„true“ erlaubt Validierung zu überspringen
<h:commandButton id="c2" value="Zurücksetzen"
immediate="true" action="#{analyse.zuruecksetzen}"/>
===geht los
before: RESTORE_VIEW 1
A: Zaphod :: 42
after: RESTORE_VIEW 1
A: Zaphod :: 42
before: APPLY_REQUEST_VALUES 2
A: Zaphod :: 42
after: APPLY_REQUEST_VALUES 2
A: :: 0
before: RENDER_RESPONSE 6
A: :: 0
after: RENDER_RESPONSE 6
A: :: 0
===is Schicht
Prof. Dr. Stephan Kleuker
306Komponentenbasierte Software-Entwicklung
Nutzung von immediate für Eingabefelder• immediate=true fordert sofortige Validierung und Konvertierung für
diese Komponente<h:inputText id="i1" value="#{analyse.ein}"
immediate="true" validatorMessage="nur 3">
<f:validateLength maximum="3"/>
</h:inputText>
===geht los
before: RESTORE_VIEW 1
A: null :: 0
after: RESTORE_VIEW 1
A: null :: 0
before: APPLY_REQUEST_VALUES 2
A: null :: 0
after: APPLY_REQUEST_VALUES 2
A: null :: 0
before: RENDER_RESPONSE 6
A: null :: 0
after: RENDER_RESPONSE 6
A: null :: 0
===is Schicht
<Return> im Eingabefeld drücken
Prof. Dr. Stephan Kleuker
307Komponentenbasierte Software-Entwicklung
Eineinhalb Schleifen
public String uebernehmen(){
return "/index.xhtml?faces-redirect=true";
}
// wie vorher
INFO: before: INVOKE_APPLICATION 5
INFO: A: Zaphod :: 42
INFO: after: INVOKE_APPLICATION 5
INFO: A: Zaphod :: 42
INFO: ===geht los
INFO: before: RESTORE_VIEW 1
INFO: A: Zaphod :: 42
INFO: after: RESTORE_VIEW 1
INFO: A: Zaphod :: 42
INFO: before: RENDER_RESPONSE 6
INFO: A: Zaphod :: 42
INFO: after: RENDER_RESPONSE 6
INFO: A: Zaphod :: 42
INFO: ===is Schicht
Ohne Redirect: rein serverseitige Weiterleitung, Client kennt Ziel nicht
Top Related