View
215
Download
0
Category
Preview:
Citation preview
Prof. Dr. Stephan Kleuker
306Komponentenbasierte Software-Entwicklung
5. JavaServer Faces (JSF) / JEE
5.1 Grundlagen
5.2 Einführendes nulltes Beispiel
5.3 Validierung
5.4 JSF mit EJB und JPA
5.5 Get
5.6 Sicherheit
5.7 Weitere JSF-Möglichkeiten
5.8 JSF-Lebenszyklus
5.9 Templates und Komponenten
5.10 Nutzung von Ajax
5.11 Testen von Web-Applikationen - Selenium
5.12 JSF-Erweiterungen
Prof. Dr. Stephan Kleuker
307Komponentenbasierte Software-Entwicklung
Literatur
• E.Burns, C. Schalk, JavaServer Faces 2.0: The CompleteReference, 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, Prentice Hall, USA, 2010
• K. Ka Iok Tong, Beginning JSF 2 APIs and JBoss Seam, Apress, Berkeley, USA, 2009
• Standard: Sun, JSR 344: JavaServer Faces 2.2, http://jcp.org/aboutJava/communityprocess/final/jsr344/index.html
• Sun, JEE7 Tutorial, http://download.oracle.com/javaee/7/tutorial/doc/
• Bücher nutzen teilweise auf Eclipse und JBoss; nicht notwendig, funktioniert fast alles mit Netbeans und Glassfish / Apache
Prof. Dr. Stephan Kleuker
308Komponentenbasierte Software-Entwicklung
5.1 Grundlagen
verschiedene Ziele von JSF-Applikationen
• Software, die Web als zusätzlichen Nutzen hat (z. B. Web-Präsenz, Kataloge, Bestellmöglichkeiten)
• verteilte Softwaresysteme, die Netz als Komponentenverbindung nutzen (z. B. B2B)
• Arbeitsplatzsoftware, die auch das Web nutzt (nahtlose Integration, Web ist „unsichtbar“)
• letztes Ziel gewinnt immer mehr Bedeutung für andere Ziele
• Aber: Nicht immer ist Web-fähige Software gewünscht!
Prof. Dr. Stephan Kleuker
309Komponentenbasierte Software-Entwicklung
Technische Herausforderungen (1/2)
auf welchem Rechner läuft welche Software
• zentraler Server oder peer-to-peer
• Client-Server, wer ist thin, wer ist fat
Browser-fähig oder standalone
• welcher Browser, welche Sicherheitseinstellungen, welche Plugins, welches HTML
• Wie bekommt Kunde Software zum Laufen, wie funktionieren Updates
• darf man, muss man offline arbeiten können
• Usability
Prof. Dr. Stephan Kleuker
310Komponentenbasierte Software-Entwicklung
Technische Herausforderungen (2/2)
Sicherheit
• wie Daten sicher verschicken, wem gehört Internet, wer muss zuhören dürfen
Performance und Stabilität
• schnelle Antworten auch bei Last
• saubere, reproduzierbare Transaktionen
• was passiert bei Netzausfall
Prof. Dr. Stephan Kleuker
311Komponentenbasierte Software-Entwicklung
Typische Entwicklungsplattformen (Ausschnitt)
.Net / Microsoft• ASP.Net (Active Server Pages, gute Abstraktion, zunächst zu
wenig Web-Server (IIS))• Silverlight (Browser-PlugIn) für RIA, mittlerweile auch lokal• Oberflächen basierend auf WPF (Windows Presentation
Forum, vektorbasiert, anfänglich zu langsam)Java• Servlets, JSP, JSF [später genauer], angegeben mit
steigenden Abstraktionsgrad sehr weit verbreitet• verschiedene neue Frameworks (z. B. Apache Wicket)• GWT (Google Widget Toolset), SW einmal in Java schreiben,
dann individuell für Browser in Javascript übersetzen• JavaFX , eigene Sprache, nutzt im Browser JREMeinung: Silverlight und JavaFX gegenüber HTML5 und aktuell noch Flash im Nachteil, JavaFX geht in andere Richtung
Prof. Dr. Stephan Kleuker
312Komponentenbasierte Software-Entwicklung
Konzept eines Seitenaufrufs
• HTML (Hypertext Markup Language)
– Auszeichnungssprache mit festgelegten tags zum Aufbau der Ausgabe
• Ebene 3/4 typisch TCP/IP, Session Ebene 5: HHTP, Darstellungsebene 6: HTML, Programmebene 7: JVM
ClientApplication Server
HTTP-Request
HTTP-Response
mit
HTML-Datei im Body
Web-
Container
EJB-
Container
Prof. Dr. Stephan Kleuker
313Komponentenbasierte 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
314Komponentenbasierte Software-Entwicklung
Web-Server mit Container nutzen
• 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
315Komponentenbasierte Software-Entwicklung
Motivation für JSF
• Die Widerverwendbarkeit von Servlets ist gering
• Innerhalb jeder JSP 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
316Komponentenbasierte Software-Entwicklung
Web-Server mit JSF- (und EJB-) Unterstützung
• Beispiele:
– Apache Tomcat
– 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
317Komponentenbasierte 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
318Komponentenbasierte 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 eingegestellt (verschwimmt damit) http://www.w3.org/2009/06/xhtml-faq.html
• Standard: http://www.w3.org/TR/xhtml1/
Prof. Dr. Stephan Kleuker
319Komponentenbasierte 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
320
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
321
Projekt einrichten (1/2)
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
322
Projekt einrichten (2/2)
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
323Komponentenbasierte Software-Entwicklung
5.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
324
<?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>
<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
325
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
326
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>
<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
327
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")
@RequestScoped
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
328
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
329
Nulltes JSF-Beispiel (7/11) - Managed Bean [3/3]@Override
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
330
Nulltes JSF-Beispiel (8/11) - web.xml [1/2]• Konfigurationsdatei für Servlets (hier benötigt)<?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
331
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
332
Nulltes JSF-Beispiel (10/11) - Projektstruktur
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
333
Nulltes JSF-Beispiel (11/11) - Ergebnis
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
334
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
335
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
336
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 7.3.1, Glassfish 4, MS IE 9, MS IE 9 Nutzereinstellungen, Win7 x64, …
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
337
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
338Komponentenbasierte Software-Entwicklung
Basisprinzip der Applikationssteuerung
• Im Controller: Aufruf einer Methode ohne Parameter vom Rückgabetyp String<h:commandButton value="Abschicken"
action="#{modul.uebernehmen}"/>
• Im Modell:public String uebernehmen(){
return "./ausgabe.xhtml";
}
• 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
Prof. Dr. Stephan Kleuker
339Komponentenbasierte 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
340
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
341Komponentenbasierte 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
342Komponentenbasierte 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
343Komponentenbasierte Software-Entwicklung
Scope-Beispiel (4/5) - Ein Nutzer, zwei Klicks
Prof. Dr. Stephan Kleuker
344Komponentenbasierte Software-Entwicklung
Scope-Beispiel (5/5) - Zwei NutzerN
utz
er
1N
utz
er
2
Zeit
Prof. Dr. Stephan Kleuker
345
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
346
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
347
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
348
ConversationScope (4/4)
• Bild jeweils nach dem Klicken
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
349
Ausgabeprobleme: Umlaute und Zeilenumbrüche
• vor Knopfnutzung danach
• danach
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
350
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
351
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>
https://wikis.oracle.com/display/GlassFish/FaqHttpRequestParameterEncoding
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
352
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
353
Zeilenumbrüche (2/2)
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
354Komponentenbasierte Software-Entwicklung
Manövrieren vor JSF 2.0
• Grundidee: Automat mit Seiten als Knoten (Zustände), Übergang findet durch String-Konstanten statt
• String-Konstanten sind Ergebnisse von (Action-) Methoden (oder stehen direkt in action="EINGEBEN")
index.xhtml
ausgabe.xhtml
Prof. Dr. Stephan Kleuker
355Komponentenbasierte Software-Entwicklung
Manövrieren vor JSF 2.0 - faces-config.xml 1/2<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="1.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_1_2.xsd">
<navigation-rule>
<from-view-id>/index.xhtml</from-view-id>
<navigation-case>
<from-outcome>ANZEIGEN</from-outcome>
<to-view-id>/ausgabe.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>/ausgabe.xhtml</from-view-id>
<navigation-case>
<from-outcome>EINGEBEN</from-outcome>
<to-view-id>/ index.xhtml </to-view-id>
</navigation-case>
</navigation-rule>
Prof. Dr. Stephan Kleuker
356Komponentenbasierte Software-Entwicklung
Manövrieren vor JSF 2.0 - Managed Bean
// statt durch Annotationen können Beans auch in faces-
// config.xml angelegt werden
public class Modul implements Serializable {
private static final long serialVersionUID = 1L;
private int nr;
private String name;
// fehlt: get- und set-Methoden für Exemplarvariablen
public Modul(){}
public String uebernehmen(){ //Action-Methode
// Zugriff auf Exemplarvariablen möglich
return "ANZEIGEN";
}
public String eingeben(){
// Zugriff auf Exemplarvariablen möglich
return "EINGEBEN";
}
...
}
Prof. Dr. Stephan Kleuker
357Komponentenbasierte Software-Entwicklung
Manövrieren vor JSF 2.0 - faces-config.xml 2/2<managed-bean>
<managed-bean-name>modul</managed-bean-name>
<managed-bean-class>entities.Modul</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
</faces-config>
Vorteil der klassischen Navigation:
• Es gibt zentrale Stelle, an der es einen Überblick über mögliche Abläufe gibt
Nachteile:
• JSF 2.0 bietet flexiblere Ablaufsteuerung
• XML ist schwer nachvollziehbar
• Konfigurationsdatei faces-config.xml bleibt trotzdem wichtig
Prof. Dr. Stephan Kleuker
358Komponentenbasierte 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
359Komponentenbasierte 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
360
<?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
361Komponentenbasierte Software-Entwicklung
Standardattribut rendered (3/3)
<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="1.2" ...>
<managed-bean>
<managed-bean-name>mo</managed-bean-name>
<managed-bean-class>entities.Mojo</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
</faces-config>
oder infaces-config.xhtml
Prof. Dr. Stephan Kleuker
362Komponentenbasierte Software-Entwicklung
Strukturierung mit panelgrid / Kommentare
• 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
• (?!) 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
363
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
364
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
365Komponentenbasierte Software-Entwicklung
5.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
366Komponentenbasierte 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
367
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
368Komponentenbasierte Software-Entwicklung
Ausgaben beim Drücken von „Abschicken“
Prof. Dr. Stephan Kleuker
369
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
370Komponentenbasierte 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
371Komponentenbasierte 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
372
Anmerkungen zu vorherigen Folien
• man kann eigene Validierungsmethoden schreiben
• man kann auch hier BeanValidation nutzen (folgt danach)
• 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-Klassen, 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 (hier gehen z. B. auch einfache Bean-Variablen mit Meldungstexten)
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
373Komponentenbasierte Software-Entwicklung
Erinnerung: Bean Validation
• nicht:
• besser:
Datenzu-griffsschicht
fachlicheEbene
Oberfläche DB
Nutzer
Java
individuelleDaten-
validierung
individuelleDaten-
validierung
individuelleDaten-
validierung
individuelleDaten-
validierung
Datenzu-griffsschicht
fachlicheEbene
Oberfläche DB
Java
Datenmodell mitValidierungsregeln
Nutzer
Prof. Dr. Stephan Kleuker
374Komponentenbasierte Software-Entwicklung
Beispiel Bean-Validation (1/6)
@Named
@RequestScoped
public class Modul implements Serializable {
private static final long serialVersionUID = 1L;
@Min(value=100, message="dreistellig")
@Max(value=999, message="dreistellig")
private int nr;
@NotNull @Modulnamenregel(verboten={"VHDL","PNP"},
groups={validators.ModulGroup.class})
private String name;
//... übliche Konstruktoren, get und set
Hinweis: Implementierung des Validierers in Libraries benötigt (Klasseneigenschaften werden nicht unterstützt!?)
Prof. Dr. Stephan Kleuker
375Komponentenbasierte Software-Entwicklung
Beispiel Bean-Validation (2/6)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {ModulnamenregelValidator.class})
@Documented
public @interface Modulnamenregel {
String message() default "Modul unerwuenscht";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String[] verboten() default {};
}
package validators;
public interface ModulGroup {}
Prof. Dr. Stephan Kleuker
376Komponentenbasierte Software-Entwicklung
Beispiel Bean-Validation (3/6)
public class ModulnamenregelValidator implements
ConstraintValidator<Modulnamenregel,String>{
private String[] niveau;
public void initialize(Modulnamenregel cA) {
this.niveau=cA.verboten();
}
public boolean isValid(String t,
ConstraintValidatorContext cvc) {
System.out.println("isValid");
for(String s:this.niveau)
if(s.equals(t)){
cvc.buildConstraintViolationWithTemplate(
"Modul in unerwuenschter Liste: "
+ Arrays.toString(niveau))
.addConstraintViolation();
return false;
}
return true;
}
}
Prof. Dr. Stephan Kleuker
377Komponentenbasierte Software-Entwicklung
Beispiel Bean-Validation (4/6) - index.xhtml
<h:body>
<h:form>
<h:panelGrid columns="3" >
<h:outputLabel for="mname" value="Modulname "/>
<h:inputText id="mname" value="#{modul.name}">
<f:validateBean disabled="false"
validationGroups="validators.ModulGroup"/>
</h:inputText>
<h:message for="mname" />
<h:outputLabel for="mnr" value="Modulnummer "/>
<h:inputText id="mnr" value="#{modul.nr}" />
<h:message for="mnr" />
<h:commandButton value="Abschicken"
action="#{modul.uebernehmen}"/>
</h:panelGrid>
</h:form>
</h:body>
Prof. Dr. Stephan Kleuker
378Komponentenbasierte Software-Entwicklung
Beispiel Bean-Validation (5/6)
Prof. Dr. Stephan Kleuker
379
Beispiel Bean-Validation (6/6)
Komponentenbasierte Software-Entwicklung
Bean Validation- (und JPA-) Realisierungen
enthalten
Prof. Dr. Stephan Kleuker
380Komponentenbasierte Software-Entwicklung
Ordentliche Softwarearchitektur
• Controller (und Bean) auch „backing bean“, Handler genannt
• bei DB-Nutzung greift Controller auf DB-Verwaltungsschicht zu; die verwaltet Bean-Objekte
• JPA einfach im Controller (besser eigener Schicht) nutzen (später schöner mit JEE als SessionBean @Stateless)
JSF-Seite<<managed-bean>>
Controller
Bean
(Entität)
#{controller.speichern}
#{controller.bearbeiten}
#{controller.bean.attribut}
Prof. Dr. Stephan Kleuker
381Komponentenbasierte Software-Entwicklung
5.4 JSF mit EJB und JPA
Ziel: einfache Persistierung von Daten (Create, Read, Update, Delete) mit Transaktionssteuerung
• DB einbinden
• Kurzer Überblick EJB
• Controller zur Seitenverwaltung
• Ausgabe dynamischer Tabellen
Prof. Dr. Stephan Kleuker
382Komponentenbasierte Software-Entwicklung
Datenbank in Server einbinden
Einschub: Nutzung von JPA bei DB im Server
• Datenbank einrichten
• Datenbank wird unter JNDI-Namen im Server registriert
• JSF-Applikation kann
über JNDI auf DB zugreifen
• JSF-Container regelt DB-Zugriff (nicht Nutzer!), typische container-managed Persistance [geht auch individuell]
Prof. Dr. Stephan Kleuker
383
beans.xml
• Für die Nutzung von CDI (Contexts and DependencyInjection) wird Datei beans.xml benötigt, die sich im Ordner web.xml befinden muss
• Wenn nicht da, erzeugen
• vorletzte Zeile ändern (statt annotated in all)
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
384
EJB-SessionBeans
Architekturvariante
• bisher einfache direkte Software-Architektur (nutzbar für kleinere und mittlere Projekte)
• Problem: Skalierung nicht unbedingt sichergestellt, da Server nicht im Detail eingreifen kann
• Problem: direkte Nutzung von JPA wäre sehr bastelig
• Lösung: Datenbankzugriff in eigene Klasse (Enterprise JavaBean, EJB) auslagern
• EJB werden vom Server verwaltet (Tempo, Skalierung)
• EJB werden in Managed Bean injiziert
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
385
EJB - Konzept
• Standardisierte Komponenten eines JEE-Servers• Klassen zur Steuerung von Prozessen und Verwaltung von
Daten, die leicht in anderen EJB-Applikationen nutzbar sind• Unterstützt mit wenig Aufwand
– Transaktionssteuerung (Transaktion pro Methodenaufruf)– Sicherheitseinstellungen
– Identifikation von Objekten zur Zusammenarbeit• Drei Arten (local und remote Varianten)
– Entity Bean (POJO, @Entity wie bereits bekannt)– Session Bean: Kommunikation mit (End-)Anwendungen
• @Stateless: bei jeder Nutzung neu erstellt• @Stateful: in einer Session nutzbar
– Message Driven Bean: asynchroner Nachrichtenablauf
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
386
Erinnerung: Verwandt mit Persistierer.java
@Stateless
public class PersistenzService {
@Inject
private EntityManager em;
public void persist(Object object) {
em.persist(object); //Exception weiterreichen
}
public Object merge(Object object) {
return em.merge(object); //Exception weiterreichen
}
public List<Mitarbeiter> findAllMitarbeiter() {
return em.createNamedQuery("Mitarbeiter.findAll")
.getResultList();
} ...
wird vom Server gefüllt, (noch) unklar woher
Session vom Container gesteuert, keine
eigene Steuerung!
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
387
Erzeugen des Persistence-Objekts über CDI
import javax.enterprise.inject.Produces;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
//@Dependent
public class MyProducers {
@Produces
@PersistenceContext(unitName = "SprinterPU")
private EntityManager em;
}
Komponentenbasierte Software-Entwicklung
Server verwaltet Verbindung, damit auch Transaktionssteuerung
Sollen Objekte erzeugt werden können, die in JEE-
Klassen per @Inject nutzbar sind
Prof. Dr. Stephan Kleuker
388
persistence.xml (Ausschnitt)
<persistence-unit name="SprinterPU" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider
</provider>
<jta-data-source>java:app/jdbc/SprinterDB</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.schema-
generation.database.action"
value="create"/>
</properties>
</persistence-unit>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
389
Zentrale Aufgabe: JSF zur Mitarbeiterverwaltung
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
390
Strategische Entscheidung
• SessionScoped braucht wesentlich mehr Speicher; wird für Status des Fensters benötigt (Alternativ zwei Fenster)
• zentrale Frage wann wird auf die Datenbank zugegriffen
– bei RequestScoped bei Erstellung eines neuen Objekts, d.h. sehr häufig
– wenn SessionScoped immer aktuell sein soll, dann auch hier sehr häufig (bei jeder Aktion) aktualisieren
– Kompromiss: am Anfang einmal Lesen und nur, wenn Änderungen nach außen gegeben werden
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
391
MitarbeiterController (1/6)
@Named
@SessionScoped
public class MitarbeiterController implements Serializable {
@Inject
PersistenzService pers;
private List<Mitarbeiter> alleMitarbeiter;
private Mitarbeiter mitarbeiter; // Hilfsobjekt für Oberfläche
private Status modus; // aktuell Status.BASIC oder Status.EDIT
private List<Mitarbeiter> alleElemente() {
return pers.findAllMitarbeiter();
}
Komponentenbasierte Software-Entwicklung
@Inject auch direkt für @Stateless-annotierte
Objekte nutzbar
Prof. Dr. Stephan Kleuker
392
MitarbeiterController (2/6)
@PostConstruct
public void init() {
this.mitarbeiter = new Mitarbeiter();
this.modus = Status.BASIC;
this.alleMitarbeiter = alleElemente();
}
public String abbrechen() {
this.mitarbeiter = new Mitarbeiter();
this.modus = Status.BASIC;
return Konstanten.MITARBEITER;
}
public boolean getImEditmodus() {
return this.modus.equals(Status.EDIT);
}
garantiert, dass dies Nach der Erstellung
und vor anderen Methoden
aufgerufen wird (Konstruktor bleibt
leer)
Zugriff von XHTML-Seite aus, da
Methode mit "get" beginnt
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
393
MitarbeiterController (3/6)
public String uebernehmen() {
FacesContext ctxt = FacesContext.getCurrentInstance();
FacesMessage ms = new FacesMessage("Erfolgreich übernommen");
try {
if (this.modus.equals(Status.EDIT)) {
this.mitarbeiter = (Mitarbeiter) pers
.merge(this.mitarbeiter);
aktualisiereMitarbeiter(this.mitarbeiter); // lokale Liste
this.modus = Status.BASIC;
} else {
this.pers.persist(this.mitarbeiter);
this.alleMitarbeiter.add(this.mitarbeiter); // lokal
}
this.mitarbeiter = new Mitarbeiter(); // neues Hilfsobjekt
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
394
MitarbeiterController (4/6)
} catch (Exception e) {
ms = Utilities.meldung(e);
this.alleMitarbeiter = alleElemente(); // hier wichtig
}
ctxt.addMessage(null, ms);
return Konstanten.MITARBEITER;
}
private void aktualisiereMitarbeiter(Mitarbeiter m) {
int position = -1;
for (int i = 0; i < this.alleMitarbeiter.size(); i++) {
if (this.alleMitarbeiter.get(i).getId() == m.getId()) {
position = i;
}
}
this.alleMitarbeiter.set(position, m);
}Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
395
MitarbeiterController (5/6)
public String loeschen(long id) {
FacesContext ctxt = FacesContext.getCurrentInstance();
FacesMessage ms = new FacesMessage("Erfolgreich gelöscht");
ctxt.getExternalContext().getFlash().setKeepMessages(true);
try {
this.pers.removeMitarbeiter(id);
loescheMitarbeiter(id); // lokal
} catch (Exception e) {
ms = Utilities.meldung(e);
this.alleMitarbeiter = alleElemente(); // hier wichtig
}
ctxt.addMessage(null, ms);
this.mitarbeiter = new Mitarbeiter();
this.modus = Status.BASIC;
return Konstanten.MITARBEITER;
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
396
MitarbeiterController (6/6)
public String editieren(long id) {
this.mitarbeiter = pers.findMitarbeiter(id);
if (this.mitarbeiter == null) {
FacesContext ctxt = FacesContext.getCurrentInstance();
FacesMessage ms = new FacesMessage("Objekt wurde"
+ "zwischenzeitlich gelöscht");
ctxt.getExternalContext().getFlash().setKeepMessages(true);
ctxt.addMessage(null, ms);
this.alleMitarbeiter = alleElemente(); // wichtig
} else {
this.modus = Status.EDIT;
}
return Konstanten.MITARBEITER;
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
397
Einschub: Ausgabe von Sammlungen in Tabellen
<h:dataTable value="#{sprintController.sprints}" var="s">
• dataTable erlaubt die Datenaufbereitung einer Collection in Tabellenform (Iterator über Daten mit getSprints(); Elemente in Variable m); grundlegende Darstellungsmöglichkeiten einstellbar
• Zur Ausgabe wird minimal <h:column> benötigt
• Attribut first für erste Zeile und rows für Zeilenzahl
• Nie, nie Elemente der alten JSTL nutzen <c:foreach>
• wenn keine Tabelle gewünscht <ui:repeat value=… var…> nutzt
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
398
Strukturierung in XHTML (Facelets)
• Templating wird unterstützt
• auch einfaches inkludieren von Seiten nutzbar, dann wird Header ignoriert
• Parameter-Übergabe möglich
• generell (leicht eingeschränkte) Nutzung von CSS möglich (Attribut für jedes Objekt oder/und generell für gerenderte Dateien)
• <div> - Blöcke (aus HTML) erleichtern Strukturierung
• generell: JavaScript für einzelne Aktionen nutzbar
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
399
Konzept der Basisseite rahmen.xhtml
<h:head>
<title>#{titel}</title>
<h:outputStylesheet library="css" name="tabelle.css" />
</h:head>
<h:body>
<div id="kopf">
<div id="knopfleiste"> … </div>
<div id="detailinformation">
<ui:insert name="uebersicht"/>
</div>
</div>
<div id="nachricht" style="height: 45px; overflow: hidden">
<h:messages globalOnly="true"/>
</div>
<div id="hauptteil">
<ui:insert name="inhalt"/>
</div>
</h:body>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
400
Ausschnitt mitarbeiter.xhtml (1/4): Kopf/Parameter
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<h:head>
<title>Mitarbeiter-Bearbeitung</title>
<h:outputStylesheet library="css" name="tabelle.css" />
</h:head
<h:body>
<ui:composition template="./resources/templates/rahmen.xhtml">
<ui:param name="titel" value="Mitarbeiter-Bearbeitung"/>
<ui:param name="sprints" value="true"/>
<ui:param name="mitarbeiter" value="false"/>
<ui:param name="kanban" value="true"/>
<ui:param name="sprintId" value="-1"/>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
401
Ausschnitt mitarbeiter.xhtml (2/4): Eingabefelder
<ui:define name="inhalt">
<h3> Mitarbeiter neu / bearbeiten </h3>
<h:panelGrid columns="3" >
<h:outputText value="ID "/>
h:outputText value="#{mitarbeiterController.mitarbeiter.id}"
rendered="#{mitarbeiterController.imEditmodus}"/>
<h:outputText value="wird vom System vergeben"
rendered="#{!mitarbeiterController.imEditmodus}"/>
<h:outputText value=" "/>
<h:outputText value="Mitarbeiternummer "/>
<h:inputText id="minr" required="true" value
="#{mitarbeiterController.mitarbeiter.minr}"
requiredMessage="eindeutige Nummer ge 0 angeben"
converterMessage="geben ganze Zahl ein"/>
<h:message for="minr" style="color:red"/>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
402
Ausschnitt mitarbeiter.xhtml (3/4): Übersicht
<h3> Aktuelle Mitarbeiter </h3>
<h:panelGrid rendered
="#{!empty mitarbeiterController.alleMitarbeiter}">
<h:dataTable border="1" frame="box"
value="#{mitarbeiterController.alleMitarbeiter}"
var="m" styleClass="tabelle"
headerClass="tabelle-kopfzeile"
rowClasses="tabelle-ungerade-zeile
,tabelle-gerade-zeile" >
<h:column >
<f:facet name="header">
<h:outputText value="ID" />
</f:facet>
<h:outputLabel value="#{m.id}"/>
</h:column>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
403
Ausschnitt mitarbeiter.xhtml (4/4): Aktionen
<h:column>
<f:facet name="header">
<h:outputText value="Mitarbeiten" />
</f:facet>
<h:commandLink value="Mitarbeiten"
action="#{mitarbeitController.fuerMitarbeiter(m)}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Editieren" />
</f:facet>
<h:commandButton value="Editieren"
action="#{mitarbeiterController.editieren(m.id)}"
immediate="true"/>
</h:column>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
404
Analyse erreichter Architektur
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
405
Kritische Analyse der erreichten Architektur
• für Projektgröße ok
• evtl. kritisch: JSF-Controller müssen recht detailliert Persistenz steuern, sollten sich aber nur um die Seite kümmern (-> Auslagern der Steuerung in ViewModels)
• evtl. kritisch JSF-Controller greifen direkt auf Entities zu (die in Data Transfer Objects (DTO) kapseln; hier auch berechnete Werte hinein)
• neues Problem: recht viel redundanter Code in Controller und ViewModel; DTOs müssen vollständiges ER-Modell abdecken
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
406
Systematische Architektur
• Ansatz: mehrere Schichten, jede Schicht redet nur mit den umgebenden Schichten
• GUI konsequent vom Rest trennen, um Schicht einfach auszutauschen
• GUI kennt keine Fachklassen, nur Strings und Zahlen
• GUI erhält View-Controller, der Zustand des GUIs kennt und Verbindung zur Geschäftslogik enthält
• Geschäftslogik hat keine Information über GUI
• Persistenz-Schicht sollte austauschbar sein
• Hier: konsequenter Einsatz von Injection
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
407
Mini-Beispiel (1/9)
• Nicht editierbare Aufgabenliste
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
408
Mini-Beispiel (2/9): Architektur
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
409
Mini-Beispiel (3/9): Projektaufbau
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
410
Mini-Beispiel (4/9): Datenmodell@Entity
public class Aufgabe implements Serializable{
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String aufgabe;
public int getId() { return id; }
public void setId(int id) {
this.id = id;
}
public String getAufgabe() { return aufgabe;}
public void setAufgabe(String aufgabe) {
this.aufgabe = aufgabe;
}
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
411
Mini-Beispiel (5/9): Persistenzschicht (1/2)
public class MyProducers {
@Produces // evtl. mit Qualifier kennzeichnen
@PersistenceContext(unitName = "ListeJSFEJBPU")
private EntityManager em;
}
• Injection passiert mit CDI (Contexts and Dependency Injection, später mehr) mit eigenem Lebenszyklus
• CDI und EJB getrennte Konzepte
• so werden EJBs dem CDI-Zyklus untergeordnet
• @Produces: angelegtes Objekt über CDI injizierbar
• Muss! bei vorgeschlagener Architektur
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
412
Mini-Beispiel (6/9): Persistenzschicht (2/2)@Stateless
public class Persistence {
@Inject
private EntityManager em;
public void persist(Object object) {
em.persist(object); //Exception weiterreichen
}
public Object merge(Object object) {
return em.merge(object); //Exception weiterreichen
}
public List<Aufgabe> findAllAufgaben() {
return em.createQuery("SELECT a FROM Aufgabe a", Aufgabe.class)
.getResultList();
}
}Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
413
Mini-Beispiel (7/9): Geschäftslogik@Dependent // hier wären fachliche Berechnungen
public class Modell implements Serializable{
@Inject
private Persistence db;
public List<String> aufgaben(){
List<String> erg = new ArrayList<>();
for(Aufgabe a:this.db.findAllAufgaben()){
erg.add(a.getAufgabe());
}
return erg;
}
public void aufgabeHinzu(String aufgabe){
Aufgabe a = new Aufgabe();
a.setAufgabe(aufgabe);
this.db.persist(a);
}
}Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
414
Mini-Beispiel (8/9): ViewModel@Named(value = "vm")
@RequestScoped
public class ViewModel implements Serializable{
private String aufgabe;
@Inject
private Modell model;
public List<String> aufgaben(){
return this.model.aufgaben();
}
public void aufgabeHinzu(){
this.model.aufgabeHinzu(this.aufgabe);
this.aufgabe = "";
}
public String getAufgabe() { return aufgabe; }
public void setAufgabe(String aufgabe) {
this.aufgabe = aufgabe;
}
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
415
Mini-Beispiel (9/9): View
<h:head>
<title>Aufgaben</title>
</h:head>
<h:body>
<h:form id ="form">
<h:outputText value="Aufgabe: "/>
<h:inputText id="aufgabe" value="#{vm.aufgabe}"/>
<h:commandButton id="hinzu" value="OK"
action="#{vm.aufgabeHinzu()}"/>
<br/>
<ui:repeat value="#{vm.aufgaben()}" var="a">
#{a} <br/>
</ui:repeat>
</h:form>
</h:body>
</html>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
416Komponentenbasierte Software-Entwicklung
5.5 Get
Aktuell: Links nur sehr eingeschränkt nutzbar, für Bookmarks nutzlos
Gewünscht: Links für Bookmarks und zum Aufruf von Funktionalität mit Parametern nutzbar
Prof. Dr. Stephan Kleuker
417
Wunsch nach GET
• aktuelle Seiten nicht für Bookmarks oder Mails geeignet
• sinnvoll wäre GET-Nutzung (ab JSF 2.0 möglich, wird erweitert, mindestens 2.2.6 von mojarra notwendig)
• Ansatz: In Seite können GET-Parameter spezifiziert werden
• aufgerufene Seite erhält Parameter und kann diese in speziellen f:metadata-Tag lesen und mit Variablen des Controllers verknüpfen
• Steuerung passiert durch neue Elemente, h:button und h:link ersetzen h:commandButton und h:commandLink
• Trick (ab und zu nutzbar): Baue vor klassische Seite neue Seite für Parameter, die alte Seite inkludiert
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
418
Beispiel: gewünschte Links
• Mitarbeiten eines Mitarbeiters
• Aufruf der Editier-Funktion (gerendert)<td><input type="button"
onclick="window.location.href='/Sprinter/faces/
mitarbeiterParam.xhtml?mid=10&actionId=2';
return false;" value="Editieren" /></td>
• actionId als Protokoll, was gemacht werden soll (1=anzeigen, 2=editieren, 3=loeschen)
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
419
h:Button genauer
<h:button value="Editieren"
outcome="mitarbeiterParam">
<f:param name="mid" value="#{m.id}" />
<f:param name="actionId" value="2" />
</h:button>
• outcome nennt aufzurufende Seite
• Parameter per GET übergeben
• aufgerufene Seite, in <f:metadata>
– Einlesen mit<f:viewParam name="mid" value="#{controller.id}" />
– Aktion, die nach dem Einlesen passieren soll <f:viewAction action="#{controller.reagiereAufGet}"/>
Komponentenbasierte Software-Entwicklung
nächste Folie
Prof. Dr. Stephan Kleuker
420
mitarbeiterParam.xhtml
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<f:metadata>
<f:viewParam name="mid"
value="#{mitarbeiterController.mitarbeiterId}"/>
<f:viewParam name="actionId"
value="#{mitarbeiterController.action}"/>
<f:viewAction action=
"#{mitarbeiterController.mitarbeiterControllerMitGet}"/>
</f:metadata>
<h:head></h:head>
<h:body>
<ui:include src="mitarbeiter.xhtml"/>
</h:body>
</html>ursprüngliche Seite
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
421
in MitarbeiterController (1/2)
@Named
@SessionScoped
public class MitarbeiterController implements Serializable {
...
private long mitarbeiterId; // fuer GET mit get/set
private int action; // ausgewaehlte Aktion
public String mitarbeiterControllerMitGet() {
this.modus = Status.BASIC;
if (this.action == 2) {
this.action = 0;
return this.editieren(this.mitarbeiterId);
}
if (this.action == 3) {
this.action = 0;
return this.loeschen(this.mitarbeiterId);
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
422
in MitarbeiterController (2/2)
if (this.action == 1) { // zeige alle Mitarbeiter
this.action = 0;
this.mitarbeiter = new Mitarbeiter();
this.alleMitarbeiter = alleElemente();
return Konstanten.MITARBEITER;
}
return null; // verhindert erneute Parameterauswertung
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
423
Einbau von get-Links in mitarbeiter.xhtml
<h:column>
<f:facet name="header">
<h:outputText value="Mitarbeiten" />
</f:facet>
<h:link value="Mitarbeiten"
outcome="mitarbeitMitarbeiterParam">
<f:param name="mid" value="#{m.id}" />
<f:param name="actionId" value="1" />
</h:link>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Editieren" />
</f:facet>
<h:button value="Editieren" outcome="mitarbeiterParam">
<f:param name="mid" value="#{m.id}" />
<f:param name="actionId" value="2" />
</h:button>
</h:column>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
424
Anmerkung Get
• vorherige Folien zeigen, wie Ansatz ohne Get-Nutzung zur Get-Nutzung erweitert werden kann
• der Ansatz ist unüblich
• generell am Anfang des Projekts entscheiden, ob zentral GET genutzt werden soll
• großer Vorteil: Link-Nutzung
• eventueller Nachteil: etwas komplexere Ablaufsteuerung und ggfls. Sicherheitsaspekte
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
425Komponentenbasierte Software-Entwicklung
5.6 Sicherheit
Vier Aufgaben
• Authentifizierung: Nutzer erkennen; klassisch über Name und Passwort
• Autorisierung: Nutzer darf nur bestimmte Aktionen durchführen, klassisch über Rechte-Rollen-System
• Vertraulichkeit: kein weiterer Nutzer (außer BKA, NSA, ...) darf Informationen mitlesen
• Unverfälschtheit: Inhalte werden zwischen Senden und Empfangen nicht verändert
• letzten beiden Punkte typischerweise durch Verschlüsselung realisiert
Prof. Dr. Stephan Kleuker
426
Nutzer – Rollen – Gruppen
• typischerweise wird ein Nutzer einer oder mehreren Gruppen (mit unterschiedlichen) Rechten zugeordnet
• zur Vereinfachung (Abstraktion) bekommt jeder Nutzer genau eine Rolle, die dann verschiedenen Gruppen zugeordnet werden kann
• jede benötigte Gruppenkombination benötigt eine Rolle
Komponentenbasierte Software-Entwicklung
Nutzer
Rolle
Gruppe
1
MNC
NC
NC M
Prof. Dr. Stephan Kleuker
427Komponentenbasierte Software-Entwicklung
Varianten zur Realisierung
• direkt von Hand codieren (mit DB-Anbindung für uns technisch kein Problem; Übertragung aber unsicher mit HTTP)
– jeder Methodenaufruf beginnt mit Berechtigungsprüfung
• deklarativ
– Möglichkeit zur abgetrennten Authentifizierung
– Seiten (Servlets, JSP, JSF) können als schützenswert markiert werden (im Deployment Descriptor)
– Zugang zu Seiten wird nur Nutzer in bestimmten Rollen gewährleistet
• deklarativ (Variante)
– Programmcode direkt mit erlaubten Rollen annotieren
Prof. Dr. Stephan Kleuker
428Komponentenbasierte Software-Entwicklung
Umsetzungsmöglichkeit in JSF
Deployment Descriptor (web.xml)
• Man kann Rollen definieren
• Man kann Seiten/Ordner Rollen zuordnen
• Man kann Art der Übertragung festlegen
und Applikationsserver-spezifisch:
• Auswahl eines Informationssystems zur Authentifizierung (Nutzer, Passwort, zugehörige Rollen), z. B. im Server selbst, Datenbank, Anbindung LDAP
• Zuordnung der Server-Gruppen zu den Rollen der JSF-Applikation (oder Zuordnung von Nutzern zu Rollen der JSF-Applikation)
Prof. Dr. Stephan Kleuker
429Komponentenbasierte Software-Entwicklung
Applikationsbeispiel - Überblick
1. Nutzer wählt Funktionalität
2. Nutzer muss sich authentifizieren
3. Prüfung ob Nutzer erlaubt und ob er benötigte Rechte hat
4a. falls ja, Zugang zur Funktionalität
4b. falls Nutzer unbekannt, ihn über Scheitern informieren
4c. falls Nutzer bekannt und zu wenig Rechte, ihn über fehlende Rechte informieren [oder wie 4b behandeln]
• Hinweis: statt geschützten Zugriff auf Startseite kann diese noch offen sein (ergonomisch, weitere Einstellungen regelbar)
• technische Probleme:
– Nutzer kann mehrere Seiten für gleiche Applikation nutzen
– Nutzer spielt mit „Back“-Button
Prof. Dr. Stephan Kleuker
430
Sicheres JSF (1/17) – Aufgabenstellung / Nutzer
• Nur registrierte Basisnutzer können Modul hinzufügen
• Nur Editierer (Admins) dürfen Modul bearbeiten
• Server unterstützen verschiedene Sicherheitsmechanismen
• direkter Eintrag von Nutzern im Server
• Anbindung an LDAP
• Nutzung eigener Tabellen (wird hier genutzt, eigener Realm, eigene Tabellen zur Verwaltung, mit verschlüsselten Passwort, genauer: http://home.edvsz.hs-osnabrueck.de/skleuker/querschnittlich/NetbeansNutzung.pdf)
Komponentenbasierte Software-Entwicklung
username password
edna edna
uwe uwe
otto otto
groupid usernme
admin edna
basic edna
basic uwe
Nutzer Gruppen
Prof. Dr. Stephan Kleuker
431
Sicheres JSF (2/17) – Ergebnis 1/3
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
432
Sicheres JSF (3/17) – Ergebnis 2/3
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
433
Sicheres JSF (4/17) – Ergebnis 3/3 - Konzept
• Web-Seiten in Ordner pro Nutzergruppe einordnen
• In web.xml regeln, welche Gruppe worauf zugreifen darf
• Standard login-Servlet einschalten und nutzen
• Ausnahmefälle regeln
• Ausloggen sicherstellen (Session deaktivieren)
• generell: inkrementelle Entwicklung
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
434
Sicheres JSF (5/17) - Projektstruktur
Komponentenbasierte Software-Entwicklung
• serverspezifische Einstellungen• Angabe von Rollen und Rechten
• zu schützende Informationen pro Gruppe
• auch Startseite geschützt
• Eingabe der Nutzererkennung
• bekannter Nutzer ohne Rechte
• nicht existenter Nutzer
Prof. Dr. Stephan Kleuker
435Komponentenbasierte Software-Entwicklung
Sicheres JSF (6/17) - serverindividuelle Vorbereitung
• Rechte-Rollen werden mit konkreten Nutzern in Datei glassfish-web.xml (vorher sun-web.xml) umgesetzt
• <security-role-mapping>
<role-name>basisnutzer</role-name>
<group-name>basic</group-name>
</security-role-mapping>
<security-role-mapping>
<role-name>editierer</role-name>
<group-name>admin</group-name>
</security-role-mapping>
• Auch Nutzer (username) direkt eintragbar
Gruppenname aus Datenbank (Spalte)
Prof. Dr. Stephan Kleuker
436
<welcome-file-list>
<welcome-file>faces/hinzufuegen/index.xhtml</welcome-file>
</welcome-file-list>
<security-constraint>
<display-name>C1</display-name>
<web-resource-collection>
<web-resource-name>Basisschutz</web-resource-name>
<description/>
<url-pattern>/faces/hinzufuegen/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<description/>
<role-name>basisnutzer</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
Komponentenbasierte Software-Entwicklung
Sicheres JSF (7/17) - JSF-Konfiguration web.xml [1/3]
Startseite im Unterordner
Regel für alle Seiten des Ordners
wer darf
HTTPS einschalten
Prof. Dr. Stephan Kleuker
437Komponentenbasierte Software-Entwicklung
Sicheres JSF (8/17) - JSF-Konfiguration web.xml [2/3]
<security-constraint>
<display-name>C2</display-name>
<web-resource-collection>
<web-resource-name>Editierschutz</web-resource-name>
<description/>
<url-pattern>/faces/editieren/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<description/>
<role-name>editierer</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
Prof. Dr. Stephan Kleuker
438
Sicheres JSF (9/17) - JSF-Konfiguration web.xml [3/4]
<login-config>
<auth-method>FORM</auth-method>
<realm-name>zugriffsRealm</realm-name>
<form-login-config>
<form-login-page>/faces/hinzufuegen/login.xhtml</form-login-page>
<form-error-page>/faces/loginError.xhtml</form-error-page>
</form-login-config>
</login-config>
<security-role>
<description/>
<role-name>basisnutzer</role-name>
</security-role>
<security-role>
<description/>
<role-name>editierer</role-name>
</security-role>
Komponentenbasierte Software-Entwicklung
Art der Anmeldung (so sicher)
im Server; Verweis auf Nutzertabellen
Login und Login-Fehler-Seite
in glassfish-web.xml eingerichtete Rollen
Prof. Dr. Stephan Kleuker
439
Sicheres JSF (10/17) - JSF-Konfiguration web.xml [4/4]
<error-page>
<error-code>403</error-code>
<location>/faces/fehler.xhtml</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/faces/fehler.xhtml</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/faces/loginError.xhtml</location>
</error-page>
</web-app>
Komponentenbasierte Software-Entwicklung
pro Fehlerart eigene Seite einstellbar
Prof. Dr. Stephan Kleuker
440
Sicheres JSF (11/17) – Nutzeranalyse [1/2]
• Hinweis: Klasse nicht immer notwendig@Named
@SessionScoped
public class Nutzer implements Serializable {
private String name;
private boolean editierer;
private boolean basisnutzer;
public boolean isEdit() {
leseNutzer();
return editierer;
}
public boolean isBasis() {
leseNutzer();
return basisnutzer;
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
441
Sicheres JSF (12/17) - Nutzeranalyse [2/2]
private void leseNutzer() {
if (name == null) {
ExternalContext ectx = FacesContext
.getCurrentInstance().getExternalContext();
name = ectx.getRemoteUser();
if (ectx.isUserInRole("editierer")) {
editierer = true;
}
if (ectx.isUserInRole("basisnutzer")) {
basisnutzer = true;
}
}
}
public String getName() {
leseNutzer();
return name;
}
}Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
442
Sicheres JSF (13/17) - Startseite
<h:commandButton value="Ausloggen"
action="#{module.ausloggen(nutzer)}"/>
// in ModulBean
public String ausloggen(Nutzer nutzer) {
System.out.println(nutzer);
ExternalContext ectx = FacesContext
.getCurrentInstance().getExternalContext();
ectx.invalidateSession();
nutzer = null;
return "/hinzufuegen/index.xhtml?faces-redirect=true";
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
443
<h:column rendered="#{nutzer.edit}">
<f:facet name="header">
<h:outputText value="Bearbeitung"/>
</f:facet>
<h:commandLink value="Editieren"
action="#{module.editieren(m)}"/>
</h:column>
Sicheres JSF (14/17) – uebersicht.xhtml
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
444Komponentenbasierte Software-Entwicklung
Sicheres JSF (15/17) - login.xhtml<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE ...>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>Einloggen</title>
</h:head>
<h:body>
<form name="POST" action="j_security_check">
Nutzer:<input type="text" name="j_username"
value="" size="15" /><br/>
Passwort:<input type="text" name="j_password"
value="" size="15" /><br/>
<input type="submit" value="Anmelden" />
</form>
</h:body>
</html>
Prof. Dr. Stephan Kleuker
445Komponentenbasierte Software-Entwicklung
Sicheres JSF (16/17) - loginError.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">
<head>
<title>Fehler</title>
</head>
<h:form>
Passt nicht
<h:commandButton value="noch mal"
action="/hinzufuegen/login.xhtml"/>
</h:form>
</html>
Prof. Dr. Stephan Kleuker
446Komponentenbasierte Software-Entwicklung
Sicheres JSF (17/17) - fehler.xhml (hier unberechtigt)
<?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>Au weia</title>
</h:head>
<h:body>
<h:form>
<h:outputText
value="Hey, #{nutzer.name}, Du kommst nit rein "/>
<h:commandButton value="zurück"
action="#{wahrheit.ausloggen(nutzer)}"/>
</h:form>
</h:body>
</html>
Prof. Dr. Stephan Kleuker
447Komponentenbasierte Software-Entwicklung
Auch Fehler 500 abfangen (Session-Timeout)
Prof. Dr. Stephan Kleuker
448Komponentenbasierte Software-Entwicklung
Sicherer Datenverkehr
• Übertragungsart wählen, dann anmelden
hinzufuegen/index.xhtml
hinzufuegen/index.xhtml
hinzufuegen/index.xhtml
(login.xhtml)
Prof. Dr. Stephan Kleuker
449Komponentenbasierte Software-Entwicklung
Session-Kontrolle / Vision: systematischere Rechte
• meist sinnvoll: Nutzerdaten in Session-Objekt speichern (außer, wenn nur erlaubt / nicht erlaubt)
• Bei erster Nutzung weitere Nutzerdaten setzen (evtl. jedes Attribut auf null prüfen, dann Boolean statt boolean)
• Bei Logout Nutzer-Objekt auf null setzen (!)
• Generell Session mit invalidateSession() beenden
@Named // Ausblick, wie es sein kann, noch nicht JSF nur EJB
@SessionScoped
@DeclareRoles({"Chef","Normalo"})
public class Wahrheit {
@RolesAllowed({"Chef","Normalo"})
public String getWahr(){
return "Der Kunde ist König";
}
Prof. Dr. Stephan Kleuker
450
5.7 Weitere JSF-Möglichkeiten
• Graphische Elemente
• Konvertierung
• Nutzung von Listenern
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
451Komponentenbasierte Software-Entwicklung
GUI-Spielereien - Boolesche Checkbox
<h:form>
<h:selectBooleanCheckbox title="gratisprobe"
value="#{guiKram.gratis}" />
<h:outputText value="Wollen Sie es gratis?"/> <br>
<h:commandButton action="./index.xhtml" value="Sichern"/>
</h:form>
private boolean gratis;
public void setGratis(boolean v){
gratis=v; System.out.println("gratis: "+gratis);}
Prof. Dr. Stephan Kleuker
452Komponentenbasierte Software-Entwicklung
GUI-Spielereien - selectManyCheckBox (1/2)
import javax.faces.model.SelectItem;
public class GUIKram {
private List<SelectItem> list= new ArrayList<SelectItem>();
private String[] array={"Ute","Uwe","Urs","Uta"};
private List<String> elemente;
@PostConstruct
public void init() {
for(String s:array) list.add(new SelectItem(s));
}
public List<String> getElemente() {
System.out.println("get");
return elemente;
}
public void setElemente(List<String> e) {
elemente = e;
System.out.println("elemente: "+elemente);
} ...
Prof. Dr. Stephan Kleuker
453Komponentenbasierte Software-Entwicklung
GUI-Spielereien - selectManyCheckBox (2/2)
<h:selectManyCheckbox id="nase" value="#{guiKram.elemente}">
<f:selectItems value="#{guiKram.list}"/>
</h:selectManyCheckbox>
<h:commandButton action="./index.xhtml" value="Sichern"/>
Prof. Dr. Stephan Kleuker
454Komponentenbasierte Software-Entwicklung
GUI-Spielereien - selectManyMenu
<h:selectManyMenu id="nas2" value="#{guiKram.elemente}">
<f:selectItems value="#{guiKram.list}"/>
</h:selectManyMenu>
<h:commandButton action="./index.xhtml" value="Sichern"/>
Prof. Dr. Stephan Kleuker
455Komponentenbasierte Software-Entwicklung
GUI-Spielereien - selectManyListBox
<h:selectManyListbox id="nas3" value="#{guiKram.elemente}">
<f:selectItems value="#{guiKram.list}"/>
</h:selectManyListbox>
<h:commandButton action="./index.xhtml" value="Sichern"/>
Prof. Dr. Stephan Kleuker
456Komponentenbasierte Software-Entwicklung
GUI-Spielereien - selectOneRadio
<h:selectOneRadio id="nas4" value="#{guiKram.el}">
<f:selectItems value="#{guiKram.list}" />
</h:selectOneRadio>
<h:commandButton action="save" value="Sichern"/>
private String el;
public void setEl(String el) {
this.el = el;
System.out.println("el: "+el);
}
Variante: el am Anfang setzen
analog:
selectOneMenu
selectOneListbox
Prof. Dr. Stephan Kleuker
457Komponentenbasierte Software-Entwicklung
Konvertierung: Datum (1/3)
<h:form>
<h:panelGrid columns="3" frame="border">
<h:outputLabel for="da" value="Datum:"/>
<h:inputText id="da" value ="#{studi.start}">
<f:convertDateTime pattern ="dd.MM.yyyy"/>
</h:inputText>
<h:message for="da"/>
<h:outputLabel value="Format:"/>
<h:outputText value ="#{studi.start}"/>
<h:outputText value ="#{view.locale}" />
<h:outputText value ="#{studi.start}">
<f:convertDateTime type ="date" />
</h:outputText >
<h:outputText value ="#{studi.start}">
<f:convertDateTime type ="time" />
</h:outputText >
<h:outputText value ="#{studi.start}">
<f:convertDateTime pattern="ddd-MMMMM-yy" />
</h:outputText >
</h:panelGrid>
</h:form>
Prof. Dr. Stephan Kleuker
458Komponentenbasierte Software-Entwicklung
Konvertierung: Datum (2/3)
@Named
@RequestScoped
public class Studi {
private Date start;
public Date getStart() {
return start;
}
public void setStart(Date start) {
this.start = start;
}
}
Prof. Dr. Stephan Kleuker
459Komponentenbasierte Software-Entwicklung
Konvertierung: Datum (3/3)
Datenübertragung schon durch Drücken von <Return>, wg. Converter
Prof. Dr. Stephan Kleuker
460Komponentenbasierte Software-Entwicklung
Konzept Konvertierung
• für Standardtypen findet automatisch eine Konvertierung mit Prüfung und eventueller Fehlermeldung statt (int)
• Man kann für Standarddatentypen einige Parameter zur Konvertierung ergänzen
• Generell besteht die Möglichkeit, eigene Konvertierer in XHTML-Seite einzubinden
• Konvertierer müssen Interfaces berücksichtigen
• Ziel: Konvertierung in zugehöriger Bean, möglichst wenig Aufwand auf Darstellungsseite
• Thema bietet sich zur Vertiefung an
Prof. Dr. Stephan Kleuker
461Komponentenbasierte Software-Entwicklung
Beispiel: Nutzung von Listenern (1/3)
<?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>Facelet Title</title>
</h:head>
<h:body>
<h:form>
<h:messages globalOnly="true" layout="table"/>
<h:panelGrid columns="2">
<h:commandButton id="c1" action="#{ac.listen}" value="c1"/>
<h:commandButton id="c2" action="#{ac.listen}" value="c2"/>
<h:commandButton id="c3" action="./index.xhtml"
actionListener="#{ac.listen}" value="c3"/>
<h:commandButton id="c4" action="./index.xhtml"
actionListener="#{ac.listen}" value="c4"/>
</h:panelGrid>
</h:form> </h:body> </html>
Prof. Dr. Stephan Kleuker
462Komponentenbasierte Software-Entwicklung
Beispiel: Nutzung von Listenern (2/3)
@Named("ac")
@RequestScoped
public class Knoeppke {
public String listen() { // zufällig gleicher Name
FacesContext fc = FacesContext.getCurrentInstance();
fc.addMessage(null, new FacesMessage("wer wars?"));
return "./index.xhtml";
}
public void listen(ActionEvent e) {
UIComponent u= e.getComponent();
FacesContext fc = FacesContext.getCurrentInstance();
fc.addMessage(null, new FacesMessage("es war "+u.getId()));
}
}
Prof. Dr. Stephan Kleuker
463Komponentenbasierte Software-Entwicklung
Beispiel: Nutzung von Listenern (3/3)
Prof. Dr. Stephan Kleuker
464Komponentenbasierte Software-Entwicklung
5.8 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
465Komponentenbasierte 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
466Komponentenbasierte 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
467Komponentenbasierte 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
468Komponentenbasierte 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
469Komponentenbasierte 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
470Komponentenbasierte 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
471Komponentenbasierte Software-Entwicklung
Hintergrund: Abarbeitung von JSF-Anfragen
Prof. Dr. Stephan Kleuker
472Komponentenbasierte 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
473Komponentenbasierte 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
474Komponentenbasierte 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; Konvertierer selbst 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
475Komponentenbasierte 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
476Komponentenbasierte 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
477Komponentenbasierte 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
478Komponentenbasierte Software-Entwicklung
JSF-Lifecycle verfolgen (1/8)
• Man kann PhaseListener schreiben, die über Phasenänderungen informiert werden
• wird in faces-config.xml festgehalten<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="2.0"
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_0.xsd">
<lifecycle>
<phase-listener>controller.ZeichPhasen</phase-listener>
</lifecycle>
</faces-config>
Prof. Dr. Stephan Kleuker
479Komponentenbasierte 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
480Komponentenbasierte 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
481Komponentenbasierte 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
482Komponentenbasierte 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
483Komponentenbasierte 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
484Komponentenbasierte 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
485Komponentenbasierte 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
486Komponentenbasierte 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
487Komponentenbasierte 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
488Komponentenbasierte 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
489Komponentenbasierte 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
Prof. Dr. Stephan Kleuker
490
Nutzung von CSS
• Verwaltung von Hilfsdateien und Bildern im Ordner resources (dieser Name ist wichtig) in Unterordnern mit beliebigen Namen
• Nutzung (mehrfach möglich in h:head)
<h:outputStylesheet library="css"
name="stil.css"/>
• Jede JSF-Komponente hat style-Attribut (oder styleClass), weitereAttribute möglich<h:messages globalOnly="true"
styleClass="infoClass"/>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
491Komponentenbasierte Software-Entwicklung
JSF und CSS (1/3)
• CSS-Dateien nutzbar
• fast allen JSF-Elementen können Style-Klassen (genauer Style-Spezifikationen) und konkrete Styles zugeordnet werden
Prof. Dr. Stephan Kleuker
492Komponentenbasierte Software-Entwicklung
JSF und CSS (2/3) – Ausschnitt aus Anwendung
<h:head>
<title>Modulübersicht</title>
<h:outputStylesheet library="css" name="stil.css"/>
</h:head>
<h:form id="f2">
<h:messages globalOnly="true" styleClass="infoClass"/>
<h:panelGrid rendered="#{!empty module.module}">
<h:dataTable value="#{module.module}" var="m"
styleClass="resultGrid" rowClasses="oddRow,evenRow">
<h:column >
<f:facet name="header">
<h:outputText value="Nummer" />
</f:facet>
...
Prof. Dr. Stephan Kleuker
493Komponentenbasierte Software-Entwicklung
JSF und CSS (3/3) - Beispielausgabe
Prof. Dr. Stephan Kleuker
494Komponentenbasierte Software-Entwicklung
5.9 Templates und Komponenten
Facelets - Motivation
• entwickelt, um JSP als View Declaration Language zu ersetzen
• Template-Ansatz um Wiederverwendung zu ermöglichen und Redundanzen zu vermeiden
• ein Ziel: Zusammensetzung einer logischen Seite aus verschiedenen Dateien unter Nutzung von Parametern
• JSF-View in XHTML-Syntax [nutzen wir immer]
• Erweiterungsmöglichkeit mit tag-Libraries (vgl. JSTL für JSP)
– Anmerkung: Vermeiden Sie möglichst die Nutzung der JSTL
• MVC-Ansatz, kein Java-Code in XHTML möglich (und nötig)
Hinweis: Chrome ab und zu bemerkenswert langsam
Prof. Dr. Stephan Kleuker
495Komponentenbasierte Software-Entwicklung
Erstes Template-Beispiel (1/5) - Template rahmen.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"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<h:head>
<h:graphicImage library="bilder" name="boah.jpg" width="200"
height="40" alt="boah"/>
<title>
<ui:insert name="titel"/>
</title>
</h:head>
<h:body>
<h:form>
<ui:insert name="rumpf"/>
<br/> <br/>
<h:commandLink value="Zurück" action="#{bsp.zurueck}"/>
</h:form>
</h:body>
</html>
Prof. Dr. Stephan Kleuker
496Komponentenbasierte Software-Entwicklung
Erstes Template-Beispiel (2/5) - Bean
@Named
@RequestScoped
public class Bsp {
public String getErgebnis(){
return "Du solltest Dich ändern";
}
public String bad(){
return "./analyse.xhtml";
}
public String good(){
return "./analyse.xhtml";
}
public String zurueck(){
return "./index.xhtml";
}
}
Prof. Dr. Stephan Kleuker
497Komponentenbasierte Software-Entwicklung
Erstes Template-Beispiel (3/5) - index.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"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<h:body>
<ui:composition template="./templates/rahmen.xhtml">
<ui:define name="titel">
Wesenstest
</ui:define>
<ui:define name="rumpf">
<h:outputText value="Was bist Du?"/>
<br/>
<h:commandButton value="Bad Guy" action="#{bsp.bad}"/>
<h:commandButton value="Good Boy" action="#{bsp.good}"/>
</ui:define>
</ui:composition>
</h:body>
</html>
Prof. Dr. Stephan Kleuker
498Komponentenbasierte Software-Entwicklung
Erstes Template-Beispiel (4/5) - analyse.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"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<h:body>
<ui:composition template="./templates/rahmen.xhtml">
<ui:define name="titel">
Ergebnis
</ui:define>
<ui:define name="rumpf">
<h:outputText value="#{bsp.ergebnis}"/>
</ui:define>
</ui:composition>
</h:body>
</html>
Prof. Dr. Stephan Kleuker
499Komponentenbasierte Software-Entwicklung
Erstes Template-Beispiel (5/5) - Nutzung
Prof. Dr. Stephan Kleuker
500Komponentenbasierte Software-Entwicklung
Aufbau genauer analysiert
• in index.xhtml<h:body>
<ui:composition template="./templates/rahmen.xhtml">
• im Template wird <h:head> gesetzt
• generell werden mit ui:composition Kinder des Wurzelknotens UIViewRoot erzeugt
• soll umgebender Text und Struktur berücksichtigt werden, ist ui:decorate zu nutzen; immer bei mehreren Templates
• Beispiel: folgender Text nur bei ui:decorate sichtbar
<h:body>
<h:outputText value="Hallo"/>
<ui:decorate template="./templates/rahmen.xhtml">
Prof. Dr. Stephan Kleuker
501Komponentenbasierte Software-Entwicklung
Nutzung von Parametern
• in rahmen.xhtml<h:commandLink value="Zurück" action="#{bsp.zurueck}"
rendered="#{!start}"/>
• in index.xhtml<ui:composition template="./templates/rahmen.xhtml">
<ui:param name="start" value="true"/>
<ui:define name="titel">
• in analyse.xhtml<ui:composition template="./templates/rahmen.xhtml">
<ui:param name="start" value="false"/>
• Parameter auch bei anderen Elementen nutzbar
Prof. Dr. Stephan Kleuker
502Komponentenbasierte Software-Entwicklung
Nutzung von ui:include (1/2) - index.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:ui="http://xmlns.jcp.org/jsf/facelets">
<ui:include src="./templates/head.xhtml">
<ui:param name="titel" value="Hallodele"/>
</ui:include>
<ui:include src="./templates/body.xhtml">
<ui:param name="text" value="rischtisch"/>
<ui:param name="obj" value="#{bsp}"/>
</ui:include>
</html>
Prof. Dr. Stephan Kleuker
503Komponentenbasierte Software-Entwicklung
Nutzung von ui:include (2/2) - head/body.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> <h:outputText value="#{titel}"/> </title>
</h:head>
</html>
<?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:body>
<h3> <h:outputText value="#{text}"/> </h3>
<h:outputText value="#{obj.ergebnis}"/>
</h:body>
</html>
Prof. Dr. Stephan Kleuker
504
Nutzung von h:graphicImage
• Verwaltung von Hilfsdateien und Bildern im Ordner WEB-INF/resources
• Nutzung
<h:graphicImage library="bilder" name="boah.jpg"
width="200" height="40" alt="boah"/>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
505Komponentenbasierte Software-Entwicklung
Nutzung von ui:debug
• Im Development- oder Debug-Modus erhält man durch Drücken von CTRL+SHIFT+d Komponentenbaum angezeigt<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<ui:debug/>
<ui:include src="./templates/head.xhtml">
Prof. Dr. Stephan Kleuker
506Komponentenbasierte Software-Entwicklung
Entwicklung eigener Komponenten
• Möglichkeit Komponenten basierend auf anderen Komponenten in XHTML zu realisieren
• konfigurierbar über Interface (hier nur Mini-Beispiel)
• vor eigener Entwicklung nachdenken:
– Realisierung mit Templates oder/und include genauso gut?
– gibt es gewünschte Komponente schon? (z. B. nachschauen unter http://jsfcentral.com/)
– Reichen nicht Validatoren, Konvertierer und evtl. PhaseListener aus?
Prof. Dr. Stephan Kleuker
507Komponentenbasierte Software-Entwicklung
Komponente (1/4) - Schnittstelle glossar.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"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:composite="http://xmlns.jcp.org/jsf/composite">
<!-- INTERFACE -->
<composite:interface>
<composite:attribute name="schlagwort" required="true"/>
<composite:attribute name="detail" required="true"/>
</composite:interface>
Prof. Dr. Stephan Kleuker
508Komponentenbasierte Software-Entwicklung
Komponente (2/4) - Implementierung
<!-- IMPLEMENTATION -->
<composite:implementation>
<h:panelGrid columns="2" rules="cols" frame="box">
<h:outputLabel value="#{cc.attrs.schlagwort}"/>
<h:panelGroup>
<p>
<h:outputText value="#{cc.attrs.detail}"/>
</p>
<composite:insertChildren/>
</h:panelGroup>
</h:panelGrid>
</composite:implementation>
</html>
Prof. Dr. Stephan Kleuker
509Komponentenbasierte Software-Entwicklung
Komponente (3/4) - Beispielnutzung index.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"
xmlns:bsp="http://xmlns.jcp.org/jsf/composite/beispiel">
<h:head>
<title>Glossar</title>
</h:head>
<h:body>
<bsp:glossar schlagwort="Adam"
detail="der, wo nich allein sein konnte"/>
<bsp:glossar schlagwort="Eva"
detail="die, wo den Apfel nich lassen konnte">
Wo ist Missing Link?
</bsp:glossar>
</h:body>
</html>
Prof. Dr. Stephan Kleuker
510Komponentenbasierte Software-Entwicklung
Komponente (4/4) - Ausgabe, Projektstruktur
Ordner muss so heißen
Prof. Dr. Stephan Kleuker
511
Return-Taste soll zur Datenübernahme führen <h:body>
<ui:remove>
Die zentralen Knoepfe der Web-Seiten muessen den Namen
"schicken" haben, damit sie angesprungen werden
</ui:remove>
<h:form id="main" style="font-family:Calibri"
onkeypress="if (event.keyCode === 13) {
document.getElementById('main:schicken')
.click();
return false;
}">
• Detailliertere Bedingung im if:if (event.keyCode === 13)
and (event.target.nodeName == 'INPUT')
and (event.target.getAttribute('type') == 'text') {
document.getElementById(‚main:lokal').click();
return false;
}Komponentenbasierte Software-
Entwicklung
Prof. Dr. Stephan Kleuker
512Komponentenbasierte Software-Entwicklung
Ausblick: Komponentenmöglichkeiten
• Im Interface wesentlich mehr als nur einfache Parameter (Attribute) definierbar
• Attribute können Verknüpfungen zu Action-Methoden bzw. Event-Listenern enthalten method-signature="java.lang.String f()"
• Übergabe weiterer Funktionalität (Methoden) an Komponente möglich für Event-Listener, Konvertierer und Validatoren
<composite:actionSource>
<composite:valueHolder>
<composite:editableValueHolder>
• Funktionalität kann dann bei inneren Komponenten der Composite-Komponente genutzt werden
Prof. Dr. Stephan Kleuker
513Komponentenbasierte Software-Entwicklung
5.10 Nutzung von Ajax
• Ajax = Asynchronous JavaScript and XML(HTTPRequest)
– klassische Webapplikation:
• Nutzer macht Eingaben
• Nutzer beendet Eingabe (Knopf, Link, <Return>-Taste)
• Informationen an Server, der wertet aus
• neu berechnete Seite wird Nutzer zur Verfügung gestellt
• interaktive Applikation (Jesse James Garret, 2004)
– Nutzer macht Eingaben
– Eingaben führen auch während der Eingabe zu Reaktionen
– Informationen werden zwischenzeitlich auf Server ausgewertet
– berechnete Teilergebnisse werden in aktuelle Seite eingeblendet
Prof. Dr. Stephan Kleuker
514Komponentenbasierte Software-Entwicklung
Ajax-Beispiel (1/3) - Bean
@Named("user")
@SessionScoped
public class Nutzer implements Serializable{
private String name = "";
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
Prof. Dr. Stephan Kleuker
515Komponentenbasierte Software-Entwicklung
Ajax-Beispiel (2/3) - index.xhtml mit f:ajax
<h:form>
<h:outputText value="Your name: " />
<h:inputText id="in" value="#{user.name}" >
<f:ajax render="greeting" event="keyup"/>
<f:validateLength minimum="0" maximum="10" />
</h:inputText>
<h:message for="in"/>
<br/>
<h:panelGroup id="greeting" >
<h:outputText value="Hello, "
rendered="#{not empty user.name}" />
<h:outputText value="x " rendered="#{empty user.name}" />
<h:outputText value="#{user.name}" />
<h:outputText value="!" rendered="#{not empty user.name}"/>
</h:panelGroup>
</h:form>
Prof. Dr. Stephan Kleuker
516Komponentenbasierte Software-Entwicklung
Ajax-Beispiel (3/3) - Ausgabe
• Start
• Nutzer tippt, dann unmittelbare Aktion
• (so) keine direkte Validierung
• Nutzer drückt Return-Taste
Prof. Dr. Stephan Kleuker
517Komponentenbasierte Software-Entwicklung
Granulare Nutzung von f:ajax
• f:ajax kann um Komponenten gelegt werden
• f:ajax kann in Komponenten ergänzt werden (z. B. h:commandButton)
• f:ajax kann in und um Komponenten genutzt werden, dann ist Effekt die Summe aller spezifizierten Effekte
• Mehrere f:ajax-Einträge für eine Komponente ergänzen sich generell
• render: IDs der Komponenten, die neu gerendert werden sollen (auch @this = dieses Element, @form = umgebende Form, @all = alle Elemente, @none = kein Element)
• execute: IDs der Komponenten, die vollständig JSF-Lebenszyklus durchlaufen sollen (auch ... s. o.)
Prof. Dr. Stephan Kleuker
518
Garantierte Aktualisierung
• Nicht alle Elemente werden sofort aktualisiert, z. B. h:commandButton
• Standardtrick: Zu verändernde Komponenten in h:Panelgroup oder h:panelgrid einordnen
<h:inputText id="nutzername" value="#{steuerung.nutzer}" >
<f:ajax render="edit" event="keyup" listener="#{…}"/>
</h:inputText>
…
<h:panelGroup id="edit">
<h:commandButton id ="editButton" value="Editieren"
action="#{…}" rendered="#{!empty steuerung.nutzer}"/>
</h:panelGroup>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
519Komponentenbasierte Software-Entwicklung
Interaktionsspielerei (1/4) - index.xhtml
<h:form>
<h:outputLabel id="be" value="#{control.beruehrt}">
<f:ajax event="mouseover" render="be pg"
listener="#{control.drueber}"/>
<f:ajax event="mouseout" render="be pg"
listener="#{control.weg}"/>
</h:outputLabel>
<h:panelGrid id="pg" columns="2">
<h:outputText value="drueber:"/>
<h:outputText value="#{control.dzahl}"/>
<h:outputText value="weg:"/>
<h:outputText value="#{control.wzahl}"/>
</h:panelGrid>
</h:form>
Prof. Dr. Stephan Kleuker
520Komponentenbasierte Software-Entwicklung
Interaktionsspielerei (2/4) - Verwaltungsklasse
@Named
@SessionScoped
public class Control implements Serializable{
private String beruehrt = "beruehr mich";
private int dzahl;
private int wzahl;
public String getBeruehrt() {return beruehrt;}
public int getDzahl() {return dzahl;}
public int getWzahl() {return wzahl;}
public void drueber() {
beruehrt = "beruehr mich nicht";
dzahl++;
}
public void weg() {
beruehrt = "beruehr mich";
wzahl++;
}
}
Prof. Dr. Stephan Kleuker
521Komponentenbasierte Software-Entwicklung
Interaktionsspielerei (3/4) – Ausgabe Firefox
Prof. Dr. Stephan Kleuker
522
Interaktionsspielerei (4/4) – Ausgabe Chrome
• Zähler laufen bei Bewegung auf dem Text los, maximaler Unterschied: eins
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
523Komponentenbasierte Software-Entwicklung
<h: Komponenten> und Events (Ausschnitt)
body X X X X X X X X X X X X
commandButton X X X X X X X X X X X X X X
commandLink X X X X X X X X X X X X X
dataTable X X X X X X X X X X
inputText X X X X X X X X X X X X X X X
inputTextArea X X X X X X X X X X X X X X X
outputLabel X X X X X X X X X X X X
panelGrid X X X X X X X X X X
selectManyMenu X X X X X X X X X X X X X X X
selectOneRadio X X X X X X X X X X X X X X X
actio
n
blur
chan
ge
clic
k
dblc
lick
focu
s
keyd
own
keyp
ress
keyu
p
load
mou
sedo
wn
mou
sem
ove
mou
seou
t
mou
seov
er
mou
seup
sele
ct
unlo
ad
valu
eCha
nge
Prof. Dr. Stephan Kleuker
524
Polling mit Primefaces (1/2)
<?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"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>Poller</title>
</h:head>
<h:body>
<h:form>
<h:outputText id="o1" value="#{zeit.datum}"/><br/>
<h:outputText id="o2" value="#{zeit.anzahl}"/>
<p:poll interval="3" update="o1,o2"/>
</h:form>
</h:body>
</html>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
525
Polling mit Primefaces (2/2)
@Named
@SessionScoped
public class Zeit
implements Serializable{
private int anzahl;
public String getDatum() {
anzahl++;
return new Date().toString();
}
public int getAnzahl() {
return anzahl;
}
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
526
Anforderung
• Kanban-Board, das immer aktuell den Stand der BacklogElemente zeigt
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
527
Ausschnitt kanban.xhtml<h:panelGroup id="kanbantabelle"
rendered="#{kanbanController.ausgewaehlt}">
<p:poll interval="5" update="kanbantabelle"/>
<h3>
<h:link value="#{kanbanController.sprint.motto}"
outcome="backlogElementParam" style="color: orange">
<f:param name="sid" value="#{kanbanController.sprint.id}" />
<f:param name="actionId" value="1" />
</h:link>
</h3>
<h:dataTable value="#{kanbanController.sprint.backlogElemente}"
var="b" ...
<h:column>
<f:facet name="header">
<h:outputText value="zu bearbeiten" />
</f:facet>
<h:button value="#{b.titel}"
rendered="#{b.phase.toString() eq 'zu bearbeiten'}"
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
528
5.11 Testen von Web-Applikationen - Selenium
• Web-Browser nutzen schwerpunktmäßig HTML zur Darstellung
• Capture & Replay-Werkzeuge, die hardgecoded Pixel und Klicks verarbeiten, eignen sich meist auch für diese Programme
• Einfaches Werkzeug für Web-Applikationen und Firefox ist Selenium IDE [hier nicht gezeigt] (http://seleniumhq.org/)
– erlaubt Capture & Replay von Nutzereingaben
– ermöglicht Tests von Elementen, Identifizierung über Idsaber auch Beschriftungen
– erlaubt den Export der aufgezeichneten Tests u. a. in JUnit (Variante: Browsersteuerung aus JUnit heraus [nächste Folien], ähnlich FEST)
– basiert auf JavaScript und Iframes
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
529
Hinweise zum Testen
• Erinnerung: White Box – Grey Box – Black Box
• Basisfunktionalität mit JUnit testen
• einige Funktionalität ohne Server testbar
• gibt einfache Server nur zur Testausführung
• Selenium WebDriver ermöglicht Test der (Web-)Oberfläche
• niemals alle Tests durch Oberflächentests ersetzen
• es gibt nie das ultimative Werkzeug; aber Familie von Werkzeugen hilft oft
• Ziel: Continuous Integration
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
530
Selenium WebDriver
• Selenium steuert Browser von Java (C#, Python, Ruby) aus
• Installation als jar-Dateien
• flexible Möglichkeiten zum Finden von GUI-Komponenten
• ideal für Regressionstests, bei wenig sich ändernden GUIs
• in fast allen Unternehmen genutzt, die Web-Applikationen herstellen
• kontinuierliche Weiterentwicklung (nicht immer alles übertragbar, Selenium -> Selenium 2)
• Grundregel: nur automatisieren, was sinnvoll und machbar ist, Rest manuell
• http://docs.seleniumhq.org/docs/
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
531
Beispielprogramm (1/3)
• Spezifikation: Zu entwickeln ist eine Applikation mit der geheime Nachrichten an einen Server übergeben und dort wieder abgefragt werden können. Genauer gibt der Nutzer eine Nachricht zusammen mit zwei Codewörtern und der Anzahl, wie oft die Nachricht abgerufen werden kann, ein. Weiterhin kann ein Nutzer über die Eingabe zweier Codewörter an gespeicherte Nachrichten kommen. Ist die Codewortkombination nicht vorhanden, wird ein Standardtext ausgegeben.
• Realisierung: Glassfish, JSF (Nutzung des Application Scope)
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
532
Beispielprogramm (2/3)
Server starten
Applikation starten
http://localhost:8080/SecretSafe/
Komponentenbasierte Software-Entwicklung
vergebene Ids:
main:verfassen
main:lesen
Prof. Dr. Stephan Kleuker
533
Beispielprogramm (3/3)Nachricht verfassen Nachricht lesen
Komponentenbasierte Software-Entwicklung
eingabe:c1
eingabe:c2
eingabe:geheim
eingabe:ab
eingabe:verschicken
abfrage:c1
abfrage:c2
abfrage:abfragen
antwort:Nachricht
Prof. Dr. Stephan Kleuker
534
Einblick in Nutzungsmöglichkeiten (1/14)public class SafeMoeglichkeitenTest {
private WebDriver driver;
private int linie = 0;
@Before
public void setUp() {
// Erzeuge neue Instanz des Browser-Treibers mit Konfiguration
// DesiredCapabilities ieCapabilities =
// DesiredCapabilities.internetExplorer();
// ieCapabilities.setCapability(InternetExplorerDriver
// .INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS, true);
// System.setProperty("webdriver.ie.driver"
// , "F:\\tmp\\IEDriverServer.exe");
driver = new FirefoxDriver();
// driver = new HtmlUnitDriver();
// driver = new ChromeDriver();
// driver = new InternetExplorerDriver(ieCapabilities);
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
535
Einblick in Nutzungsmöglichkeiten (2/14)
• Klasse WebDriver als zentrale Steuerungsmöglichkeit
• Erzeugt neue Browser-Instanz
• Browser muss auf dem System installiert sein, nutzt keine weiteren Einstellungen des aktuellen Nutzers (leeres Profil)
• werden kontinuierlich weiterentwickelt
• (früher reichte driver = new InternetExplorerDriver(); )
• bisheriges Angebot (unterschiedliche Qualität): HtmlUnitDriver(), FirefoxDriver(), InternetExplorerDriver(), ChromeDriver(),
• OperaDriver durch andere Entwickler, IPhoneDriver nur zusammen mit Apple-XCode-Umgebung, AndroidDriver mit Android-Entwicklungsunterstützung
• Hintergrund: Selenium lange Zeit nur mit Firefox nutzbar
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
536
Einblick in Nutzungsmöglichkeiten (3/14)
• Zentrale Hilfsklasse für GUI-Komponenten: WebElement• nur zur Veranschaulichung: Ausgabemöglichkeit
private void zeigeElemente(List<WebElement> liste){
System.out.println("----"+(++linie));
if (liste != null) {
for (WebElement w : liste) {
System.out.println(" " + w.getTagName()
+ "::" + w.getAttribute("type")
+ "::" + w.getAttribute("name")
+ "::" + w.getAttribute("value")
+ "::" + w.getText()
+ "::" + w.getLocation() + "::" +w.isEnabled());
}
}
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
537
Einblick in Nutzungsmöglichkeiten (4/14)
• Überblick über generierte HTML-Seite
• In Entwicklung sinnvolle Ids/Namen vergeben
• JSF: Ids eindeutig
• Zugriff auch ohne Ids machbar („drittes Imput-Element“)Komponentenbasierte Software-
Entwicklung
Prof. Dr. Stephan Kleuker
538
Einblick in Nutzungsmöglichkeiten (5/14)
@Test
public void testBeispielvarianten()
throws InterruptedException, IOException {
// Seite aufrufen
driver.get("http://localhost:8080/SecretSafe/");
List<WebElement> liste =
driver.findElements(By.tagName("input"));
zeigeElemente(liste);
----1
input::hidden::main::main::::(0, 0)::true
input::submit::main:verfassen::Nachricht verfassen::::(8, 129)::true
input::submit::main:lesen::Nachricht lesen::::(8, 153)::true
input::hidden::javax.faces.ViewState::2158484851038199978:-
1608245938470041174::::(0, 0)::true
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
539
Einblick in Nutzungsmöglichkeiten (6/14)
List<WebElement> inp = driver.findElements(
By.xpath("//input"));
zeigeElemente(inp);
----2
input::hidden::main::main::::(0, 0)::true
input::submit::main:verfassen::Nachricht verfassen::::(8, 129)::true
input::submit::main:lesen::Nachricht lesen::::(8, 153)::true
input::hidden::javax.faces.ViewState::2158484851038199978:-
1608245938470041174::::(0, 0)::true
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
540
Einblick in Nutzungsmöglichkeiten (7/14)
• Viele weitere LokalisierungsmöglichkeitenMethod Summary
static By className(java.lang.String className)
static By cssSelector(java.lang.String selector)
WebElement findElement(SearchContext context)
List<WebElement> findElements(SearchContext context)
static By id(java.lang.String id)
static By linkText(java.lang.String linkText)
static By name(java.lang.String name)
static By partialLinkText(java.lang.String linkText)
static By tagName(java.lang.String name)
static By xpath(java.lang.String xpathExpression) http://selenium.googlecode.com/svn/trunk/docs/api/java/org/openqa/selenium/By.html
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
541
Einblick in Nutzungsmöglichkeiten (8/14)
• Steuerungsmöglichkeiten mit submit(), click(), weiteren Eingabemöglichkeiten
WebElement element =
driver.findElement(By.name("main:verfassen"));
System.out.println(element.getTagName()
+ "::" + element.getAttribute("type")
+ "::" + element.getAttribute("name")
+ "::" + element.getAttribute("value"));
element.click();
input::submit::main:verfassen::Nachricht verfassen
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
542
Einblick in Nutzungsmöglichkeiten (9/14)
System.out.println(driver.findElement(By.tagName("body"))
.getText());
Codewort 1:
Codewort 2:
geheime Nachricht:
wie oft abrufbar:
Zur Startseite
// Hilfsvariable für folgende Berechnung
List<WebElement> labels =
driver.findElements(By.tagName("input"));
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
543
Einblick in Nutzungsmöglichkeiten (10/14)
• es besteht die Möglichkeit JavaScript direkt auszuführen
• Mächtige Möglichkeiten, z. B. um Skripte zu starten oder Seite zu analysieren
• hier mit Übergabe und Rückgabe
@SuppressWarnings("unchecked")
List<WebElement> inputs2 = (List<WebElement>)
((JavascriptExecutor)driver).executeScript(
"var labels = arguments[0]; "
+ "var inputs = []; "
+ "for (var i=0; i < labels.length; i++){"
+ " inputs.push(document.getElementById("
+ " labels[i].getAttribute('name'))); "
+ "} "
+ "return inputs;", labels);
zeigeElemente(inputs2);
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
544
Einblick in Nutzungsmöglichkeiten (11/14)
• Ausgabe zur letzten Folie
form::null::eingabe::null::Codewort 1:
Codewort 2:
geheime Nachricht:
wie oft abrufbar:
Zur Startseite::(8, 109)::true
input::text::eingabe:c1::::::(92, 109)::true
input::text::eingabe:c2::::::(92, 131)::true
input::text::eingabe:geheim::::::(138, 153)::true
input::text::eingabe:ab::0::::(120, 175)::true
input::submit::eingabe:verschicken::Verschicken::::(8,
197)::true
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
545
Einblick in Nutzungsmöglichkeiten (12/14)Object[] werte = {"input", "text"};
@SuppressWarnings("unchecked")
List<WebElement> inputs3 = (List<WebElement>)
((JavascriptExecutor) driver).executeScript(
"var tmp = document.getElementsByTagName(arguments[0]); "
+ "var erg = []; "
+ "for (var i=0; i<tmp.length; i++){"
+ " if(tmp[i].type==arguments[1]){"
+ " erg.push(tmp[i])"
+ " }"
+ "}; "
+ "return erg;", werte);
input::text::eingabe:c1::::::(92, 109)::true
input::text::eingabe:c2::::::(92, 131)::true
input::text::eingabe:geheim::::::(138, 153)::true
input::text::eingabe:ab::0::::(120, 175)::true
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
546
Einblick in Nutzungsmöglichkeiten (13/14)
// Gibt Seitentitel auf Konsole aus
System.out.println("Titel der Seite ist: "
+ driver.getTitle());
// Bildschirmfoto
File screenshot = ((TakesScreenshot)driver)
.getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(screenshot,
new File("bild"+new Date().getTime()+".png"));
Assert.assertTrue(driver.getTitle().contains("Pssst"));
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
547
Einblick in Nutzungsmöglichkeiten (14/14)
• nach mehren Testläufen
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
548
Projektaufbau
• zentral benötigte Bibliotheken im Selenium-Download
Komponentenbasierte Software-Entwicklung
(alle) benötigt; evtl.
testng weglassen
benötigt (aktuelle
Variante)
Prof. Dr. Stephan Kleuker
549
Weitere Funktionalität
• Wechsel zwischen Fenstern und zwischen Frames
• Möglichkeit vorwärts und zurück zu navigieren
• Nutzung von Cookies
• (Versuch der) Unterstützung von Drag und Drop
• Proxy-Nutzung
• Einstellung von Wartezeiten
• Warten auf das Erscheinen von HTML-Elementen (wichtig in Richtung AJAX und HTML5)
• Zusammenspiel mit Selenium IDE zur Testaufzeichnung
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
550
Achtung: Viele Einstiegsfallen
• generell gute Einarbeitungsmöglichkeit durch gute Dokumentation
• trotzdem viele kleine Fehlerquellen, die Entwicklungsprozess bremsen können
• Beispiel: Tests ziehen auf anderen Rechner um
• wichtiges Detail aus der Doku "The browser zoom level must be set to 100% so that the native mouse events can be set to the correct coordinates." (nicht erster Google-Treffer)
• teilweise weitere Browser-Einstellungen beachten
• Browser-Updates benötigen teilweise Selenium-Updates
• Fazit: Testrechner nie zu anderen Zwecken nutzen, Konfiguration sichern
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
551
Weitere Steuerungsvarianten
• Warten, bis Element vorhanden ist(new WebDriverWait(driver, 10)).until(
new ExpectedCondition<WebElement>(){
@Override
public WebElement apply(WebDriver d) {
return d.findElement( By.name("j_idt8:j_idt10"));
}});
• Steuerungsvariante mit JavaScriptWebElement but =
driver.findElement(By.name("j_idt8:j_idt10"));
((IJavaScriptExecutor)driver)
.executeScript("arguments[0].fireEvent('onclick');", but);
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
552
Test des Beispiels (1/6)
public class SecretSafeTest {
private WebDriver driver;
private static String text1;
private static String text2;
private File f = new File("C:\\Users\\x\\AppData\\"
+ "Roaming\\Mozilla\\Firefox\\Profiles\\zm12egmo.default");
private FirefoxProfile profile = new FirefoxProfile(f);
@BeforeClass
public static void setupClass() {
text1 = "" + Math.random(); // nicht ganz QS-sauber
text2 = "" + Math.random();
}
@Before
public void setUp() {
driver = new FirefoxDriver(profile);
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
553
Test des Beispiels (2/6)@After
public void tearDown() {
driver.quit();
}
private void startSeite(){
driver.get("http://localhost:8080/SecretSafe");
}
private void eingabeSeite(){
startSeite();
driver.findElement(By.name("main:verfassen")).click();
}
private void ausgabeSeite(){
startSeite();
driver.findElement(By.name("main:lesen")).click();
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
554
Test des Beispiels (3/6)private void feldFuellen(String name, String wert){
driver.findElement(By.name(name)).clear();
driver.findElement(By.name(name)).sendKeys(wert);
}
private void textEingeben(String text1, String text2,
String geheim, int versuche){
eingabeSeite();
feldFuellen("eingabe:c1",text1);
feldFuellen("eingabe:c2",text2);
feldFuellen("eingabe:geheim",geheim);
feldFuellen("eingabe:ab",""+versuche);
driver.findElement(By.name("eingabe:verschicken")).click();
Assert.assertTrue(driver.findElement(By.tagName("body"))
.getText().contains("Eintrag erfolgreich"));
}
private void textEingeben(String geheim, int versuche){
textEingeben(text1, text2, geheim, versuche);
}Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
555
Test des Beispiels (4/6)
private void textErfolgreichSuchen(String text1,
String text2, String geheim){
ausgabeSeite();
feldFuellen("abfrage:c1",text1);
feldFuellen("abfrage:c2",text2);
driver.findElement(By.name("abfrage:abfragen")).click();
Assert.assertTrue(driver.findElement(By.tagName("body"))
.getText().contains(geheim));
}
private void textErfolglosSuchen(String text1, String text2){
ausgabeSeite();
feldFuellen("abfrage:c1",text1);
feldFuellen("abfrage:c2",text2);
driver.findElement(By.name("abfrage:abfragen")).click();
Assert.assertTrue(driver.findElement(By.tagName("body"))
.getText().contains("Treffen um 730 in KN2"));
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
556
Test des Beispiels (5/6)private void textErfolgreichSuchen(String geheim){
textErfolgreichSuchen(text1, text2, geheim);
}
private void textErfolglosSuchen(){
textErfolglosSuchen(text1, text2);
}
@Test
public void testErfolglos(){
textErfolglosSuchen();
}
@Test
public void testEintragenUndLesen(){
textEingeben("TextText", 3);
textErfolgreichSuchen("TextText");
textErfolgreichSuchen("TextText");
textErfolgreichSuchen("TextText");
textErfolglosSuchen();
}Komponentenbasierte Software-
Entwicklung
Prof. Dr. Stephan Kleuker
557
Test des Beispiels (6/6)
@Test
public void testLinkLesen(){
ausgabeSeite();
driver.findElement(By.linkText("Zur Startseite")).click();
Assert.assertTrue(driver.findElement(By.tagName("body"))
.getText().contains("Was wollen Sie machen?"));
}
@Test
public void testLinkSchreiben(){
eingabeSeite();
driver.findElement(By.linkText("Zur Startseite")).click();
Assert.assertTrue(driver.findElement(By.tagName("body"))
.getText().contains("Was wollen Sie machen?"));
}
// weitere Tests sinnvoll
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
558
Testarchitektur
• Tests müssen für Änderbarkeit konzipiert werden
• häufig: viele Tests für eine konkrete Version geschrieben, nach leichten Änderungen der zu testenden Software werden Tests als unwartbar weggelegt
• Problem: ähnlich wie Software-Architektur wird Test-Architektur benötigt
• ein Ansatz: jeweils eigene Steuerungsklasse für eine Web-Seite, Tests arbeiten nur auf diesem Abstraktionslevel
• kleine Änderungen führen zu kleinen Änderungen in der Steuerungsklasse und keinen Änderungen bei deren Nutzern
• durch Abstraktion muss nicht jeder Tester Selenium können
• -> Tests müssen von qualifizierten Software-Entwicklern geschrieben werden
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
559Komponentenbasierte Software-Entwicklung
Design-Tests
• Browser stellen identische Inhalte leicht verändert da
• technisch überflüssig, aber wichtig für den Zeitgeist: modische Design-Anpassungen
• Für IT-Profi: Sisyphos-Arbeit; Test mit unterschiedlichen Browsern
• Direkte Hilfsmittel:
– Lunascape: ein Browser, bei dem man zwischen drei Maschinen umschalten kann IE (Trident)+Firefox (Gecko)+Chrome, Safari (Webkit)
– Windows: USB-portable Browser ohne Installationsnotwendigkeit (verändern keine Einstellungen): Firefox, Chrome, Opera, …
• evtl. auch Capture & Replay mit Selenium zum inhaltlichen Test
Prof. Dr. Stephan Kleuker
560Komponentenbasierte Software-Entwicklung
5.12 JSF-Erweiterungen
• graphische Möglichkeiten in JSF recht schlicht (wesentlich besser als gezeigt!), Erweiterungsbibliotheken sinnvoll
• wesentliche frei nutzbare Komponentenframeworks:– RichFaces: http://www.jboss.org/richfaces– Primefaces: http://www.primefaces.org/
• alternative JSF-Implementierung (Standard: Mojarra):– Apache MyFaces: http://myfaces.apache.org/
• kritisch: Frameworks nur teilweise kompatibel, gilt auch für Framework mit Implementierung
• kritisch: in Details teilweise deutliche Darstellungsunterschiede in verschiedenen Browsern
• Hinweis: hier nur Ideen und notwendige Konfiguration• NetBeans bietet Nachlademöglichkeit; hier nicht genutzt um
einfacher IDE wechseln zu können
Prof. Dr. Stephan Kleuker
561Komponentenbasierte Software-Entwicklung
Nutzung von PrimeFaces
• unterstützt JSF 2, Projekt ab November 2008• Dokumentation war mal kostenpflichtig• Installationsergänzung in web.xml:<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>Resource Servlet</servlet-name>
<servlet-class>
org.primefaces.resource.ResourceServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Resource Servlet</servlet-name>
<url-pattern>/primefaces_resource/*</url-pattern>
</servlet-mapping>
Prof. Dr. Stephan Kleuker
562Komponentenbasierte Software-Entwicklung
Beispiel: Editorspielerei (1/4) - Managed Bean
@Named
@SessionScoped
public class Text implements Serializable {
private String inhalt;
public String getInhalt() {
return inhalt;
}
public void setInhalt(String inhalt) {
this.inhalt = inhalt;
}
}
Prof. Dr. Stephan Kleuker
563Komponentenbasierte Software-Entwicklung
Beispiel: Editorspielerei (2/4) - index.xhtml
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>Editor</title>
</h:head>
<h:body>
<h:form >
<p:editor id="ed" width="600px" height="120px"
value="#{text.inhalt}" widgetVar="editor"/>
<p:commandButton update="o1,o2" async="true"
value="p Uebernehmen" onclick="editor.saveHTML();"/><br/>
<h:commandButton value="h Uebernehmen"/><br/>
<h:outputText id="o1" escape="true"
value="Inhalt: #{text.inhalt}"/><br/>
<h:outputText id="o2" escape="false"
value="Inhalt: #{text.inhalt}"/>
</h:form>
</h:body>
</html>
Prof. Dr. Stephan Kleuker
564Komponentenbasierte Software-Entwicklung
Beispiel: Editorspielerei (3/4) - h: (Seite lädt neu)
Prof. Dr. Stephan Kleuker
565Komponentenbasierte Software-Entwicklung
Beispiel: Editorspielerei (4/4) - p: (Aktualisierung)
Prof. Dr. Stephan Kleuker
566Komponentenbasierte Software-Entwicklung
Anmerkungen zum Beispiel
• Normalerweise unterstützt p:commandButton Ajax direkt (nur bei p:editor Problem, deshalb Workaround mit widgetVar)
• <h:head> immer benötigt
• Generell: Genaue Analyse der oft sehr vielen Attribute einer Komponente notwendig
• Mischung von h: und p: oft (nicht immer) möglich
• Man muss mit Eigenarten der Komponenten leben (bei Großprojekten und Auswahlentscheidungen schwierig)
• detaillierte Dokumentation als PDF-Download http://primefaces.googlecode.com/files/primefaces_users_guide_4_0_edtn2.pdf
Prof. Dr. Stephan Kleuker
567
Versionsänderung - Beispiel
• Versionswechsel 2.0.2 zu 2.2.1 führt zu anderer Darstellung• Ungleichheit p: und h: korrigiert• andere Attribute statt height="120px" nun height="120"
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
568Komponentenbasierte Software-Entwicklung
JSF - es geht noch weiter
• JSF Expression Language– einfacher Zugriff auf Maps, die Umgebungsdaten, wie
param, requests, cookies und initParam haben– Nutzung von flash-Variable als Zwischenspeicher
• Erweiterung der Expression Language • Möglichkeit GET zu nutzen (und damit Bookmarks)
– <h:button> und <h:link>• JSF hat klare Regeln wie Ressourcen verwaltet werden
(Bilder, Templates, ...) • integrierte, nutzbare JavaScript-API• Viele weitere JavaScript-Möglichkeiten (u. a. h:outputScript)• weitere Möglichkeiten des Event-Handlings, z. B. eigene
Klasse reagiert auf Event (z. B. Phasenwechsel)• Ergänzung/Erweiterung des Exception Handlings• Lokalisierung, Mehrsprachlichkeit
Recommended