View
220
Download
0
Category
Preview:
Citation preview
Prof. Dr. Stephan Kleuker
313Komponentenbasierte Software-Entwicklung
6. JEE /JSF / EJB / CDI
6.1 Grundlagen
6.2 Einführendes nulltes Beispiel
6.3 Validierung
6.4 JSF mit EJB und JPA
6.5 Get
6.6 Sicherheit
6.7 Weitere JSF-Möglichkeiten
6.8 JSF-Lebenszyklus
6.9 Templates und Komponenten
6.10 Nutzung von Ajax
6.11 Testen von Web-Applikationen - Selenium
6.12 JSF-Erweiterungen
Prof. Dr. Stephan Kleuker
314Komponentenbasierte Software-Entwicklung
Literatur
• E.Burns, C. Schalk, JavaServer Faces 2.0: The Complete Reference, Mc Graw Hill, New York, 2010
• M. Marinschek, M. Kurz, G. Müllan, JavaServer Faces 2.0, dpunkt, Heidelberg, 2010 (im Wesentlichen auch: http://tutorials.irian.at/jsf/semistatic/introduction.html)
• D. Geary, C. Horstmann, Core JavaServer Faces, 3. Auflage, PrenticeHall, USA, 2010
• Standard: Sun, JSR 344: JavaServer Faces 2.2, http://jcp.org/aboutJava/communityprocess/final/jsr344/index.html
• M. Çalışkan, O. Varaksin, PrimeFaces Cookbook - Second Edition, Packt Publishing, Birmingham UK, 2015
• Sun, JEE7 Tutorial, http://download.oracle.com/javaee/7/tutorial/doc/
• Bücher nutzen teilweise Eclipse und JBoss/WildFly; nicht notwendig, funktioniert fast alles mit Netbeans und Glassfish / Apache
Prof. Dr. Stephan Kleuker
315Komponentenbasierte Software-Entwicklung
6.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
316Komponentenbasierte 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
• mobil, nativ
• Usability
Prof. Dr. Stephan Kleuker
317Komponentenbasierte 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
318Komponentenbasierte 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
319Komponentenbasierte 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
320Komponentenbasierte 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
321Komponentenbasierte 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
322Komponentenbasierte Software-Entwicklung
Motivation für JSF
• Die Widerverwendbarkeit von Servlets ist gering
• Innerhalb jeder JSP (Java ServerPage) bzw. jedes Servlets müssen ähnliche Schritte ausgeführt werden
• Nur ein Teil der Applikationslogik kann in Tag-Bibliotheken gekapselt werden
• Workflow ist in der Applikationslogik versteckt, dadurch schwer nachvollzierbar
• ab JSF 2.0 sehr flexible Gestaltungsmöglichkeiten, u. a. AJAX-Einbettung
• Hinweis: wir betrachten nur Grundkonzepte (-> mehr evtl. Semesteraufgabe)
Prof. Dr. Stephan Kleuker
323Komponentenbasierte Software-Entwicklung
Web-Server mit JSF- (und EJB-) Unterstützung
• Beispiele:
– Apache Tomcat / TomEE
– BEA WebLogic Server
– IBM WebSphere (Apache Geronimo)
– JBoss Wildfly
– Oracle WebLogic
– Glassfish (Oracle, Referenzimplementierung)
Unterschiedliches Tempo bei der Unterstützung neuer JEE-Versionen
Prof. Dr. Stephan Kleuker
324Komponentenbasierte 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
325Komponentenbasierte Software-Entwicklung
XHTML
• Idee: HTML nicht XML-konform und zu viele Freiheiten
• XHTML in fast allen Elementen wie HTML
• XHTML basierend auf XML-Schema leichter zu verarbeiten
• Unterschiede zu HTML (Ausschnitt)
– Tags und Attribute müssen klein geschrieben werden
– Attributwerte immer in Anführungsstrichen boarder="7"
– korrekte XML-Syntax: <br/> nicht <br>
– generell saubere Terminierung <input ... />
• XHTML2 wegen HTML5 eingestellt (verschwimmt damit) http://www.w3.org/2009/06/xhtml-faq.html
• Standard: http://www.w3.org/TR/xhtml1/
Prof. Dr. Stephan Kleuker
326Komponentenbasierte 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
327
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
328
Projekt einrichten (1/2)
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
329
Projekt einrichten (2/2)
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
330Komponentenbasierte Software-Entwicklung
6.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
331
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>Moduleingabe</title>
</h:head>
<h:body>
<h:form id="form" >
<h:outputText value="Modulname "/>
<h:inputText id="mname" value="#{modul.name}"
required="true"/><br/>
<h:outputText value="Modulnummer "/>
<h:inputText id="mnr" value="#{modul.nr}"
required="true"/><br/>
<h:commandButton value="Abschicken"
action="#{modul.uebernehmen}"/>
</h:form>
</h:body>
</html>
Nulltes JSF-Beispiel (2/11) - Start index.xhtml
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
332
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
333
Nulltes JSF-Beispiel (4/11) - Ausgabe ausgabe.xhtml<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>Modulausgabe</title>
</h:head>
<h:body>
<h:form id="form">
<h:outputText value="Modulname: "/>
<h:outputText id="mname" value="#{modul.name}"/> <br/>
<h:outputText value="Modulnummer: "/>
<h:outputText id="mnr" value="#{modul.nr}"/><br/>
<h:commandButton value="Zur Eingabe"
action="#{modul.eingeben}"/>
</h:form>
</h:body>
</html>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
334
Nulltes JSF-Beispiel (5/11) - Managed Bean [1/3]
package entities;
import java.io.Serializable;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
@Named("modul") // nur im Minimalbeispiel hat eine Entitaet
@RequestScoped // einen zugeordneten Scope
public class Modul implements Serializable {
private static final long serialVersionUID = 1L;
private int nr;
private String name;
public Modul(){}
public Modul(int nr, String name) {
this.nr = nr;
this.name = name;
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
335
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
336
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
337
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
338
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
339
Nulltes JSF-Beispiel (10/11) - Projektstruktur
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
340
Nulltes JSF-Beispiel (11/11) - Ergebnis
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
341
beans.xml
• Datei garantiert korrekte Abarbeitung von Annotationen
• Datei ist Konfigurationsdatei von Contexts- and DependencyInjection (CDI)
• einige Teile CDI werden in Veranstaltung nebenbei eingeführt
• Datei muss in Projekten selbst erzeugt werden
• <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="all">
</beans>
Komponentenbasierte Software-Entwicklung
Wert muss von Hand von „annotated“ auf „all“ gesetzt werden
Prof. Dr. Stephan Kleuker
342
beans.xml in NetBeans erzeugen
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
343
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
344
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
345
Einschub: IE 9 - Lösung
• Session-Informationen können statt im Server auch im Client gehalten werden
• entlastet Server, ist aber für Nutzer langsamer
• Ergänzung in web.xml:
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
• Anmerkung: fehlt systematische Fehleranalyse: NetBeans 6.3.1, Glassfish 4, MS IE 9, MS IE 9 Nutzereinstellungen, Win7 x64, …
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
346
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
347Komponentenbasierte 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"; // Endung weglassbar
}
• Methode kann natürlich abhängig von Variablen unterschiedliche Seiten liefern
• Beachten: Navigation kann in den Tiefen des Codes verschwinden ( -> Konstanten nutzen, Architektur)
• gibt XML-basierte Variante
Prof. Dr. Stephan Kleuker
348Komponentenbasierte 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
349
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
350Komponentenbasierte 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
351Komponentenbasierte 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
352Komponentenbasierte Software-Entwicklung
Scope-Beispiel (4/5) - Ein Nutzer, zwei Klicks
Prof. Dr. Stephan Kleuker
353Komponentenbasierte Software-Entwicklung
Scope-Beispiel (5/5) - Zwei NutzerN
utz
er
1N
utz
er
2
Zeit
Prof. Dr. Stephan Kleuker
354
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
355
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
356
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
357
ConversationScope (4/4)
• Bild jeweils nach dem Klicken
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
358
Ausgabeprobleme: Umlaute und Zeilenumbrüche
• vor Knopfnutzung danach
• danach
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
359
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
360
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
361
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
362
Zeilenumbrüche (2/2)
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
363
Kein Konstruktor in Managed Beans (Scopes)
• generell vieles nebenläufig
• unklar, wann Objektvariablen und andere Objekte genau erzeugt werden
• Races möglich: Zugriff auf Objekt, das gerade Konstruktor durchläuft
• alle Scopes können Methoden mit folgenden Annotationen nutzen
• @PostConstruct: nachdem das Objekt erzeugt wurde; sinnvoll hier Variablen initialisieren; Nutzung statt Konstruktor
• @PreDestroy: bevor Scope abläuft (aufräumen); auch wenn Session beendet wird
• Anmerkung: für kritische Ressourcen sinnvoll Ergänzung zu @PreDestroy zum Aufräumen
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
364
Nutzung @PostConstruct und @PreDestroy (1/5)@SessionScoped
public class Session implements Serializable{
private int zaehler;
public Session(){
System.out.println("Session Konstruktor: " + new Date());
}
@PostConstruct
public void init(){
System.out.println("Session PostConstruct: " + new Date());
}
@PreDestroy
public void destroy(){
System.out.println("Session PreDestroy: " + new Date());
}
public int plus(){
return ++this.zaehler;
}
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
365
Nutzung @PostConstruct und @PreDestroy (2/5)@RequestScoped
@Named
public class Bean implements Serializable{
@Inject
private Session session;
public Bean(){
System.out.println("Bean Konstruktor: " + new Date()); }
@PostConstruct
public void init(){
System.out.println("Bean PostConstruct: " + new Date());}
@PreDestroy
public void destroy(){
System.out.println("Bean PreDestroy: " + new Date());}
public void mach(){ // ohne Rückgabe, dann auf Seite bleiben
System.out.println("mach: " + this.session.plus());
}
} Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
366
Nutzung @PostConstruct und @PreDestroy (3/5)
• index.xhtml<h:body>
<h:form id="form">
<h:commandButton id="b" value="mach"
action="#{bean.mach}"/>
</h:form>
</h:body>
• Sessiondauer in web.xml<session-config>
<session-timeout>
1
</session-timeout>
</session-config>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
367
Nutzung @PostConstruct und @PreDestroy (4/5)Bean Konstruktor: Sat Nov 19 16:20:44 CET 2016
Session Konstruktor: Sat Nov 19 16:20:44 CET 2016
Bean PostConstruct: Sat Nov 19 16:20:44 CET 2016
Session Konstruktor: Sat Nov 19 16:20:44 CET 2016
Session PostConstruct: Sat Nov 19 16:20:44 CET 2016
mach: 1
Bean PreDestroy: Sat Nov 19 16:20:44 CET 2016
Bean Konstruktor: Sat Nov 19 16:20:53 CET 2016
Bean PostConstruct: Sat Nov 19 16:20:53 CET 2016
mach: 2
Bean PreDestroy: Sat Nov 19 16:20:53 CET 2016
Bean Konstruktor: Sat Nov 19 16:20:57 CET 2016
Bean PostConstruct: Sat Nov 19 16:20:57 CET 2016
mach: 3
Bean PreDestroy: Sat Nov 19 16:20:57 CET 2016
Session PreDestroy: Sat Nov 19 16:22:13 CET 2016
FATAL: JSF1073: javax.faces.application.ViewExpiredException
FATAL: viewId:/index.xhtml - Ansicht /index.xhtml konnte nicht
wiederhergestellt werden.
Komponentenbasierte Software-Entwicklung
nach Klick (oder längerem Warten)
Prof. Dr. Stephan Kleuker
368
Nutzung @PostConstruct und @PreDestroy (5/5)
• kleine Änderung in Bean-Klassepublic Bean(){
System.out.println("mach: " + this.session.plus());
System.out.println("Bean Konstruktor: " + new Date());
}
• möglicher Effekt bei erstem Klick:Warnung: #{bean.mach}: java.lang.NullPointerException
javax.faces.FacesException: #{bean.mach}:
java.lang.NullPointerException
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
369Komponentenbasierte 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
370Komponentenbasierte 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
371Komponentenbasierte 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
372Komponentenbasierte 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
373Komponentenbasierte 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
374Komponentenbasierte 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
375
<?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
376Komponentenbasierte Software-Entwicklung
Standardattribut rendered (3/3)
Prof. Dr. Stephan Kleuker
377Komponentenbasierte 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
378
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
379
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
380Komponentenbasierte Software-Entwicklung
6.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
381Komponentenbasierte 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
382
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
383Komponentenbasierte Software-Entwicklung
Ausgaben beim Drücken von „Abschicken“
Prof. Dr. Stephan Kleuker
384
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
385Komponentenbasierte 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
386Komponentenbasierte 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
387
Anmerkungen zu vorherigen Folien
• man kann eigene Validierungsmethoden schreiben
• man könnte auch hier BeanValidation nutzen (folgt danach), muss dies aber selbst zu passenden Ausgaben umwandeln
• FacesContext ist der zentrale Zugang zu den Innereien von JSF
• Zugriff bis auf genutzte Servlets mit deren Parametern möglich
• leider auch eine Art Voodoo-Klasse, da fast die gesamte JSF-Steuerung manipulierbar und so keine JSF-Schicht mehr richtig existiert
• FacesContext macht Code-Wiederverwendung mit anderen Technologien fast unmöglich (hier gehen z. B. auch einfache Bean-Variablen mit Meldungstexten)
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
388Komponentenbasierte 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
389Komponentenbasierte 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
390Komponentenbasierte 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
391Komponentenbasierte 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
392Komponentenbasierte 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
393Komponentenbasierte Software-Entwicklung
Beispiel Bean-Validation (5/6)
Prof. Dr. Stephan Kleuker
394
Beispiel Bean-Validation (6/6)
Komponentenbasierte Software-Entwicklung
Bean Validation- (und JPA-) Realisierungen
enthalten
Prof. Dr. Stephan Kleuker
395Komponentenbasierte 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
396Komponentenbasierte Software-Entwicklung
6.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
397Komponentenbasierte Software-Entwicklung
Datenbank in Server einbinden (1/2)
Einschub: Nutzung von JPA bei DB im Server
• Datenbank einrichten
• Data Source einrichten
Prof. Dr. Stephan Kleuker
398Komponentenbasierte Software-Entwicklung
Datenbank in Server einbinden (2/2)
• 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
399
beans.xml
• Für die Nutzung von CDI (Contexts and DependencyInjection) wird Datei beans.xml benötigt, die sich im Ordner WEB-INF befinden muss
• Wenn nicht da, erzeugen
• vorletzte Zeile ändern (statt annotated in all)
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
400
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
401
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
402
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
403
Mini-Beispiel (1/9)
• Nicht editierbare Aufgabenliste
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
404
Mini-Beispiel (2/9): Architektur
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
405
Mini-Beispiel (3/9): Projektaufbau
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
406
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
407
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
408
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
409
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
410
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
411
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
412
@Dependent
@Dependent
public class Modell implements Serializable{ … }
• Annotation gehört zu CDI (später)
• Injiziertes Objekt übernimmt Scope des nutzenden Objekts
• es werden dann ausschließlich Objekte dieser Klasse vom CDI-Framework erzeugt; nie selbst den Konstruktor aufgerufen
… // in anderer Klasse@Inject
private Modell model;
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
413
Fallstudie Sprinter
• Verwaltung von Sprints, in denen BacklogElementeabgearbeitet werden
• Zu jedem BacklogElement gibt es einen Menge von Mitarbeiten (Teilaufgaben)
• Jeder Mitarbeiter kann an mehreren Mitarbeiten mitwirken
• Das Werkzeug soll Informationen über den aktuellen Stand von BacklogElementen und Sprints ausgeben
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
414
Hintergrund
• Im agilen Vorgehensmodell wird Projekt in mehrere Sprints aufgeteilt (diese Aufgaben stehen im Product-Backlog)
• Arbeiten in einem Sprint werden Backlog-Elemente genannt
• Backlog-Elemente werden von Mitarbeitern abgearbeitet
Prozess:
1. aus Sprint-Länge und Anzahl der Mitarbeiter wird maximal verfügbare Stundenzahl berechnet
2. Festlegung der (Sprint-)Backlog-Elemente, die im Sprint zu erledigen sind, mit Aufwandsschätzung durch Mitarbeiter
3. Festlegung der durchführenden Mitarbeiter mit Arbeitsanteil
4. ggfls. Anpassung der für Sprint zur Verfügung stehenden Stundenanzahl
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
415
Ausschnitt
Komponentenbasierte Software-Entwicklung
(eines Sprints)
Prof. Dr. Stephan Kleuker
416
Datenmodell
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
417
Zentrale Aufgabe: JSF zur Mitarbeiterverwaltung
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
418
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
419
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
Transaktion vom Container gesteuert,
keine eigene Steuerung!
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
420
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
Anmerkung: Diese (wenig intuitive) Hilfsklasse ermöglicht flexible Verknüpfung von EJB und CDI
Prof. Dr. Stephan Kleuker
421
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
422
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
423
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
424
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
425
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
426
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
427
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
428
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
429
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
430
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
431
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
432
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
433
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
434
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
435
Analyse erreichter Architektur
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
436
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
Recommended