Upload
tranhanh
View
245
Download
0
Embed Size (px)
Citation preview
Prof. Dr. Stephan Kleuker
380
5. (RESTful) Web Services
• JavaScript Object Notation
• JSONP
• Idee: Web-Services
• Idee: RESTful
• erste Services
• GET, POST
• Clients
• Response
• Path-Parameter
• Aufrufparameter
• Architektur von REST-Applikationen
• Fallstudie (GET, POST, PUT, DELETE)
• Ausblick
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
381
Ausblick auf weitere Themen
Komponentenbasierte Software-Entwicklung
Browser
Datenbank
JPA
EJBBean
Validation
CDIScope
JSFRESTful
WebService
Web Sockets
12
3
Prof. Dr. Stephan Kleuker
382
Einstieg JSON
• JavaScript Object Notation (http://json.org/)
• textuelles Austauschformat, abgeleitet aus JavaScript{ "name": "Tony Stark",
"alter": 42,
"firma": { "name": "Stark Industries",
"ort": "New York, N.Y"
},
"freunde":["Steve Rogers", "Bruce Banner"]
}
• Sammlung von
– (Name: Wert)-Paaren
– Arrays von Werten
• Werte können wieder aus beiden Elementen bestehen
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
383
Vereinheitlichung von JSON in Java
in JEE 7 ergänzt:
• JSR 353: JavaTM API for JSON Processing (23.5.2013)
• JSR 374: JavaTM API for JSON Processing 1.1 (24.5.2017)
https://jcp.org/en/jsr/detail?id=374
• Referenzimplementierung jsonp https://jsonp.java.net/
• in Glassfish seit 4.0 enthalten
zwei zentrale APIs
• Object Model API; sehr analog zum DOM API für XML parsing
• Streaming API; sehr analog zum StAX API
• unabhängig von Programmiersprachen nutzbar
• kompakter als XML (ähnlich gut/schlecht menschenlesbar)
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
384
Beispiel: JSON-Object lesen (1/2)
public static void main(String[] args) {
String daten =
"{ \"name\": \"Tony Stark\","
+ " \"alter\": 42,"
+ " \"firma\": { \"name\": \"Stark Industries\","
+ " \"ort\": \"New York, N.Y\""
+ "},"
+ "\"freunde\":[\"Steve Rogers\", \"Bruce Banner\", 42]"
+ "}";
JsonReader reader = Json.createReader(new StringReader(daten));
JsonObject tony = reader.readObject();
reader.close();
//Set<String> namen = tony.keySet(); // geht auch
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
385
Beispiel: JSON-Objekt lesen (2/2)
System.out.println("Name : " + tony.getString("name"));
System.out.println("Alter : " + tony.getInt("alter"));
JsonObject firma = tony.getJsonObject("firma");
System.out.println("Firmenname : " + firma.getString("name"));
System.out.println("Umsatz : " + firma.getInt("umsatz", 20));
JsonArray freunde = tony.getJsonArray("freunde");
for (JsonValue freund : freunde) {
System.out.println(freund + " * " + freund.getValueType());
}
}
Name : Tony Stark
Alter : 42
Firmenname : Stark Industries
Umsatz : 20
Steve Rogers * STRING
Bruce Banner * STRING
42 * NUMBER
Default, wenn nicht da
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
386
Beispiel: JSON-Objekt von Hand erstellen
public static void main(String[] args) {
JsonObject personObject = Json.createObjectBuilder()
.add("name", "Bruce Banner")
.add("alter", 44)
.add("firma",
Json.createObjectBuilder()
.add("name", "Shield")
.add("ort", "unbekannt")
.build())
.add("freunde",
Json.createArrayBuilder()
.add("James Howlett")
.add("Ben Grimm")
.build())
.build();
System.out.println("Object: " + personObject);
}
Object:
{"name":"Bruce
Banner","alter":44,"f
irma":{"name":"Shield
","ort":"unbekannt"},
"freunde":["James
Howlett","Ben
Grimm"]}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
387
Ausschnitt Klassendiagramm
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
388
Beispiel: Stream-Bearbeitung von JSON
// daten: siehe JSON lesen
JsonParser parser = Json
.createParser(new StringReader(daten));
while (parser.hasNext()) {
Event event = parser.next();
System.out.print(event + ": ");
switch (event) {
case KEY_NAME:
System.out.print(parser.getString());
break;
case VALUE_NUMBER:
System.out.print(parser.getInt());
break;
}
System.out.println("");
}
START_OBJECT:
KEY_NAME: name
VALUE_STRING:
KEY_NAME: alter
VALUE_NUMBER: 42
KEY_NAME: firma
START_OBJECT:
KEY_NAME: name
VALUE_STRING:
KEY_NAME: ort
VALUE_STRING:
END_OBJECT:
KEY_NAME: freunde
START_ARRAY:
VALUE_STRING:
VALUE_STRING:
VALUE_NUMBER: 42
END_ARRAY:
END_OBJECT:Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
389
Binding
• Binding schafft automatische Umwandlungsmöglichkeit von A nach B und von B nach A
• ohne Binding muss die Umwandlung (marshalling) manuell erfolgen, bei Netztransport ggfls. Rückumwandlung notwendig (unmarshalling)
• Java-Objekt von und nach XML löst JAXB
• JSR 222: JavaTM Architecture for XML Binding (JAXB) 2.0, https://jcp.org/en/jsr/detail?id=222
• wichtig Umwandlungsprozess konfigurierbar
• Java-Objekt von und nach JSON noch nicht standardisiert (für JEE 8 angekündigt)
• Referenzimplementierung für Glassfish (Stand Ende 2013) ist MOXy (übersetzt JAXB-Annotationen nach JSON)
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
390
Beispiel: Vorbereitung einer Entitäts-Klasse für JSON
@XmlRootElement
public class Punkt implements Serializable {
private int x;
private int y;
public Punkt() {} // wichtig
public Punkt(int x, int y) {this.x = x; this.y = y;}
public int getX() {return x;}
public int getY() {return y;}
public void setX(int x) {this.x = x;}
public void setY(int y) {this.y = y;}
@Override
public String toString() {return "[" + x + "," + y + "]";}
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
391
Annotationen zur Steuerung der Übersetzung
@XmlElement(name=“rufname") // Key-Umbenennung
public String name;
@XmlTransient // nicht übertragen
public int alter;
• man beachte, dass man erhaltenes Objekt auch noch mit vorherigen Methoden modifizieren kann
• Übersetzung noch nicht standardisiert (aktuell MOXy, Teil von EclipseLink)
• da manuelle JsonObject-Erzeugung nicht sehr aufwändig und sehr flexibel, wird es gute Alternative bleiben
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
392
Hintergrund Web Services
• zentraler Wunsch: einfache Nutzung von Software über das Netz
• unabhängig wo sich ein Rechner befindet
• unabhängig von der Programmiersprache
SOAP-basierte WebServices
• jeder Service hat eindeutige Kennung (URI, Uniform Resource Identifier)
• Schnittstellenbeschreibung WSDL
• typisch: XML-basierte Kommunikationsprotokolle
• typisch: Verbindung mit SOA
• hier nicht wichtig, aber SOA ≠ SOAP ≠ Web Service
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
393
Hintergrund: Service Oriented Architecture
Service-
Verzeichnis
Service-
Anbieter
Service-
Nutzer
3. Anfragen
4. Antworten
SOAP
WSDL
HTTP
UDDI
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
394
Zwischenfazit SOA
• Vision: auf Grundlage von Geschäftsprozessmodellierungen kann individuelle Software für ein Unternehmen entstehen
• Realität: machbar, wenn alles auf einem Hersteller basiert
• Realität: UDDI hat in fast allen Projekten nicht stattgefunden (SOA ist auch Super Overhyped Acronym)
• aber: WebServices basierend auf SOAP haben als Kommunikationskonzept zentrale Bedeutung bekommen
• gilt als relativ langsam
• aber: Unternehmen nutzen es um MS-basierte Oberfläche mit JEE-realisiertem Server zu verbinden
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
395
RESTful (Representational State Transfer)
• Idee von der Interaktion zwischen Rechnern bleibt
• REST ist ein Architekturstil für verteilte Hypermedia-Systeme
• Protokoll: nutze Möglichkeiten von HTTP
– GET: lese Information (SELECT)
– POST: neue Information (INSERT)
– PUT: ändere Information (UPDATE)
– DELETE: lösche Information (DELETE)
• Klammern deuten Ähnlichkeit zu Datenbankoperationen an
• Grundlage: Dissertation Roy Fielding „Architectural Styles and the Design of Network-based Software Architectures “
• http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
396
(An)forderungen zur REST-Nutzung
• Adressierbarkeit: Jede Ressource eindeutig über URL identifizierbar
• Ressourcenorientierung: alle Anfragen beziehen sich auf Ressourcen, URL kann verarbeitenden Dienst und Ress-Id enthalten
• flexible Darstellung: maschinell lesbar, z. B: JSON, XML, HTML
• Zustandsbasiert: Interaktion entspricht endlichen Automaten (Interaktion führt zu Folgezustand, weitere Schritte klar definiert)
• Statuslos: Alle Infos zur Bearbeitung in Anfrage enthalten
• Standardisiert: Zugriff über standardisierte Operatoren (hier: Get, Post, Put, Delete, Patch, Head, Options)
• Literatur: D. Abts, Masterkurs Client/Server-Programmierung in Java, 4. Auflage, Springer Vieweg, 2015
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
397
Woher kommt „ Representational State Transfer“
Client fordert Information mit Hilfe einer URL (URI) an.
Eine Repräsentation der Information zusammen mit möglichen Bearbeitungsschritten, wird als Ergebnis zurückgegeben (z. B. in Form eines JSON-Objekts), Client hat Informationszustand.
Client nutzt Hyperlinks in Ergebnis um weitere Informationen anzufordern.
Neues Ergebnis versetzt Client in einen neuen Informationszustand.
ResourceClient
http://www.scrumsprinter.de/sprint/42
{ “id”: 42,
“name”: “Prototyp”,
“elemente”: [ …
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
398
HATEOAS – saubere REST-Architektur
„Hypermedia as the Engine of Application State“
• Client kennt nur die Basis-URI des Dienstes
• Server leitet durch Informationszustände der Anwendung durch Bekanntgabe von Wahlmöglichkeiten (Hyperlinks), genauer: welche Folgeaufrufe sind wie möglich
• HTTP-Kommunikationsprotokoll selbst bleibt zustandslos
• Grundregel: GET, PUT, DELETE (auch PATCH, HEAD, OPTIONS) sind idempotent; führen zum gleichen Ergebnis, egal wie oft sie im gleichen Informationszustand aufgerufen werden
• häufig genutzter Trick: POST auch zur partiellen Aktualisierung
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
399
Richardson Maturity Model
• Bewertung inwiefern WebService REST-Bedingungen erfüllt
• Stufe 0: RESTless, nutzt URI, HTTP als Transportprotokoll für RPC-Aufrufe
• Stufe 1: Ressourcen, jede Ressource hat URI, diese wird zur direkten Ansprache genommen
• Stufe 2: HTTP-Verben, GET, POST, … werden in der von REST geforderten Form genutzt, Antworten mit HTTP-Statuscodes
• Stufe 3: RESTful - Hypermedia, zustandsbasiert, klare Information in welchem Zustand sich Bearbeitung befindet und welche nächsten Aktionen (mit Parameterbereichen) möglich sind
• Anmerkung: Diskutabel, aber oft liegt die Praxis zwischen Level 1 und 3, oft 2 (RESTbasiert)
• Literatur: z. B. https://martinfowler.com/articles/richardsonMaturityModel.html
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
400
HATEOAS: Beispiel
Komponentenbasierte Software-Entwicklung
z1
z2
b b
a
a
Prof. Dr. Stephan Kleuker
401
Wer nutzt es (Beispiele)?
• Hinweis: Öfter wird gegen die geforderte Reinform von RESTful WebServices verstoßen, und normale Anfragemöglichkeit mit GET als RESTful oder REST-basiert bezeichnet
• Google Maps
• Google AJAX Search API
• Yahoo Search API
• Amazon WebServices
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
402
Standardisierung in Java
viele Implementierungen
• Restlet http://www.restlet.org/
• Apache CXF http://cxf.apache.org/
• Project Zero http://www.projectzero.org
• GlassFish Jersey https://jersey.dev.java.net/ (Referenz)
• JBoss RESTeasy http://www.jboss.org/resteasy/
Standardisierung für Java:
• JSR 311: JAX-RS (10.10.2008)
• JSR 339: JAX-RS 2.0 (24.5.2013)
• JSR 370: JavaTM API for RESTful Web Services (JAX-RS 2.1), (22.8.2017), https://www.jcp.org/en/jsr/detail?id=370
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
403
JAX-RS aktivieren
• in JEE-aware Servern reicht folgendes ausimport javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
@ApplicationPath("resources")
public class ApplicationConfig extends Application {
}
• statt „resources“ auch „“ möglich, wenn nicht gleichzeitig JSF genutzt wird („Pfade beißen sich“)
• ist generell im .war-File
• sonst Konfiguration als Servlet nötig
• Beschreibung in web.xml
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
404
JAX-RS aktivieren (Alternative)
@ApplicationPath("resources")
public class ApplicationConfig extends Application {
@Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new java.util.HashSet<>();
try { // customize Jersey 2.0 JSON provider:
Class jsonProvider = Class
.forName("org.glassfish.jersey.moxy.json.MoxyJsonFeature");
resources.add(jsonProvider);
} catch (ClassNotFoundException ex) {}
addRestResourceClasses(resources);
return resources;
}
private void addRestResourceClasses(Set<Class<?>> resources) {
resources.add(hello.HelloWorld.class);
}
}
Anbieter von Services
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
405
erste REST-basierte WebServices
@Path("/helloworld")
public class HelloWorld {
public HelloWorld() { }
@GET
@Produces("text/html")
public String getHtml() {
return "<html><body><h1>Hello, World!!</h1></body></html>";
}
@GET
@Produces(MediaType.TEXT_PLAIN)
public String getText() {
return "Tach Welt";
}
}Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
406
detaillierte Analyse@Path("/helloworld")
• Gibt Aufrufpfad an, hier resources/helloworld
• Pfad wird an Projektpfad, z. B. /vlRESTAnfang, angehängt
• könnte auch nur an einzelnen Methoden stehen
• kann auch zusätzlich an Methoden stehen, so dass sich der Pfad verlängert
@GET
@Produces("text/html")
• Annotationen aus javax.ws.rs
• HTTP-Befehl und Ergebnistyp (mögliche Ergebnistypen, mehrere MIME-Typen [Multipurpose Internet Mail Extension])
• nachfolgender Methodenname spielt keine Rolle!
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
407
direkter Aufruf
• bei GET ist direkter Aufruf im Browser möglich
• aber, das ist ein sehr sehr untypisches Szenario
• typisch:
– Aufruf direkt aus einer Web-Seite, meist mit JavaScript
– Aufruf aus anderer Software heraus mit Mitteln der jeweiligen Programmiersprache (z. B. java.net.URL)
• NetBeans: kein Haken bei „Display Browser on Run“
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
408
Detaillierte Analyse mit cURL
• generell jedes Programm zur Erzeugung von HTTP-Aufrufen und Analyse der Ergebnisse geeignet
• Kommando-Zeile mit cURLhttp://curl.haxx.se/download.html
• Für etwaige Parameter muss auch URL in Anführungsstrichen stehen
• viele Browser unterstützen direkt bei solchen Tests
• weiteres Tool: Postman https://www.getpostman.com/Komponentenbasierte Software-
Entwicklung
Prof. Dr. Stephan Kleuker
409
Nutzung automatischen Marshallings - GET
• verschiedene Rückgabetypen bedienbar (praktisch sinnvoll?)@GET
@Produces({MediaType.TEXT_XML, MediaType.APPLICATION_JSON})
public Punkt getJSon2() {
return new Punkt(42,43); // war @XMLRootElement annotiert
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
410
Nutzung automatischen Unmarshallings - POST
@POST
@Produces(MediaType.TEXT_PLAIN)
@Consumes(MediaType.APPLICATION_JSON)
public String postit(Punkt p){
System.out.println(p);
return "ok";
}
• weitere Parameter im JSON-Objekt führen zu Fehlern
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
411
zentrale Klassen Client und Response
• RESTful Web Services werden typischerweise aus anderer Software aufgerufen
• dies ist natürlich auch in Java möglich; vor JAX-RS 2.0 aber proprietäre Lösungen der Anbieter
• https://jersey.java.net/download.html
• jetzt Klasse javax.ws.rs.client.Client
• Bei der Nutzung von RESTful Web Services können verschiedene Klassen als Typen für Parameter und Rückgabe genutzt werden
• Hilfreich ist Klasse javax.ws.rs.core.Response
• Server erzeugt Response-Objekt
• Client kann problemlos Response-Objekt lesen
• Response ist ein Stream, muss auch geschlossen werden
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
412
Hilfsmethode zur genaueren Analyse von Response
private void details(Response res) {
System.out.println("-----------------\n"
+ "AllowedMethods : " + res.getAllowedMethods() + "\n"
+ "Entity Class: " + res.getEntity().getClass() + "\n"
+ "Language : " + res.getLanguage() + "\n"
+ "Location : " + res.getLocation() + "\n"
+ "Mediatype : " + res.getMediaType() + "\n"
+ "Links : " + res.getLinks() + "\n"
+ "Status : " + res.getStatus() + "\n"
+ "Date : " + res.getDate() + "\n"
+ "Class : " + res.getClass() + "\n"
+ "Inhalt : " + res.readEntity(String.class));
res.close();
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
413
Kleine Beispiele (1/7)
• Anmerkung: Zeigt Service-Nutzung, zeigt nichts von RESTpublic class ClientAnalyse {
private Client client;
private WebTarget userTarget;
public ClientAnalyse() {
Client client = ClientBuilder.newClient();
userTarget = client
.target("http://localhost:8080/vlRESTAnfang"
+ "/resources/helloworld");
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
414
Kleine Beispiele (2/7)
public void analyse1() {
Response res = userTarget.request("text/html").get();
details(res);
}
AllowedMethods : []
Entity Class: class org.glassfish.jersey.client.HttpUrlConnector$2
Language : null
Location : null
Mediatype : text/html
Links : []
Status : 200
Date : Fri Dec 18 15:39:22 CET 2015
Class : class org.glassfish.jersey.client.InboundJaxrsResponse
Inhalt : <html lang="en"><body><h1>Hello, World!!</h1></body></html>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
415
Kleine Beispiele (3/7)
public void analyse1() {
Response res = userTarget.request(MediaType.TEXT_PLAIN).get();
details(res);
}
AllowedMethods : []
Entity Class: class org.glassfish.jersey.client.HttpUrlConnector$2
Language : null
Location : null
Mediatype : text/plain
Links : []
Status : 200
Date : Wed May 14 18:55:35 CEST 2014
Class : class org.glassfish.jersey.client.ScopedJaxrsResponse
Inhalt : <html lang="en"><body><h1>Hello, World!!</h1></body></html>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
416
Kleine Beispiele (4/7)
public void analyse3() {
Response res = userTarget
.request(MediaType.APPLICATION_JSON).get();
details(res);
}
AllowedMethods : []
Entity Class: class org.glassfish.jersey.client.HttpUrlConnector$1
Language : null
Location : null
Mediatype : application/json
Links : []
Status : 200
Date : Wed May 14 18:55:35 CEST 2014
Class : class org.glassfish.jersey.client.ScopedJaxrsResponse
Inhalt : {"x":42,"y":43}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
417
Kleine Beispiele (5/7)
public void analyse4() {
Response res = userTarget.request(MediaType.TEXT_XML).get();
details(res);
}
AllowedMethods : []
Entity Class: class org.glassfish.jersey.client.HttpUrlConnector$1
Language : null
Location : null
Mediatype : text/xml
Links : []
Status : 200
Date : Wed May 14 19:08:13 CEST 2014
Class : class org.glassfish.jersey.client.ScopedJaxrsResponse
Inhalt : <?xml version="1.0" encoding="UTF-8"
standalone="yes"?><punkt><x>42</x><y>43</y></punkt>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
418
Kleine Beispiele (6/7)
public void analyse5() {
Builder buil = this.userTarget.request(MediaType.TEXT_PLAIN);
Entity e = Entity.entity(new Punk(3, 4)
, MediaType.APPLICATION_JSON);
System.out.println(e + " : " + e.getEntity());
String res = buil.post(e, String.class);
System.out.println(res);
}
javax.ws.rs.client.Entity@52aa911c : [3,4]
ok
• Anmerkung: Klasse Punk wie Punkt, sogar ohne XMLRootElement-Annotation , aber Serializable
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
419
Kleine Beispiele (7/7)
public void analyse6() {
Builder buil = this.userTarget.request(MediaType.TEXT_PLAIN);
Entity e = Entity.json(new Punk(2,3));
System.out.println(e + " : " + e.getEntity());
String res = buil.post(e, String.class);
System.out.println(res);
}
Entity{entity=[2,3], variant=Variant[mediaType=application/json,
language=null, encoding=null], annotations=[]} : [2,3]
ok
• Klasse Entity bietet einige Marshalling-Methoden
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
420
flexible Dienststrukturen
• generell soll man aus Antworten auf weitere Abfragemöglichkeiten schließen können
• /helloworld/kunden/
Frage nach Kunden: Sammlung der Namen aller Kunden
• /helloworld/kunden/Hoeness/
Frage nach Kunden mit Namen: alle Eigenschaften des Kunden
• /helloworld/kunden/Hoeness/konten
Frage nach Konten eines benannten Kunden: Sammlung aller Konten des Kunden
• /helloworld/kunden/Hoeness/konten/42
Frage nach Kontonummer eines benannten Kunden: alle Eigenschaften des Kontos dieses Kunden
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
421
Beispiel: Umsetzung von Pfaden (1/4)
@Path("helloworld")
public class HelloWorld {
// Kundenname, Sammlung von Konten (Nummer, Betrag)
private Map<String, Map<Integer, Long> > kunden;
public HelloWorld() {
// zufaellige Beispieldaten
Map<Integer,Long> tmp = new HashMap<>();
tmp.put(42,32000000L);
kunden = new HashMap<>();
kunden.put("Hoeness", tmp);
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
422
Beispiel: Umsetzung von Pfaden (2/4)
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/kunden/{user}/konten/{id}")
public JsonObject getKontostand(
@PathParam("user") String user
, @PathParam("id") int id) {
JsonObjectBuilder erg = Json.createObjectBuilder();
Map<Integer,Long> kunde = kunden.get(user);
if(kunde == null){
return erg.add("fehler", "kein Kunde").build();
}
Long summe = kunde.get(id);
if(summe == null){
return erg.add("fehler", "kein Konto").build();
}
return erg.add("summe", summe).build();
}Komponentenbasierte Software-
Entwicklung
Prof. Dr. Stephan Kleuker
423
Beispiel: Umsetzung von Pfaden (3/4)
public static void main(String[] a){
String[] verdaechtig = {"Rummenigge", "Hoeness"};
int[] nummern = {42,43};
Client client = ClientBuilder.newClient();
for(String v:verdaechtig){
for (int n:nummern){
WebTarget target = client.target("http://localhost:8080"
+ "/vlRESTAnfang/resources/helloworld/kunden/"
+ v + "/konten/" + n);
JsonObject erg = target
.request(MediaType.APPLICATION_JSON)
.get(JsonObject.class);
System.out.println(erg);
}
}
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
424
Beispiel: Umsetzung von Pfaden (4/4)
{"fehler":"kein Kunde"}
{"fehler":"kein Kunde"}
{"summe":32000000}
{"fehler":"kein Konto"}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
425
Umsetzung von Pfaden
@Path("/kunden/{user}/konten/{id}")
• Einbau von Pfadvariablen, auf die in Parameterliste mit @PathParam("user") zugegriffen werden kann
• einfache Java-Typen, typischerweise int, long, String nutzbar; Konvertierung automatisch
• Pfadvariablen in der Klassenannotation können dann in jedem Methodenkopf genutzt werden
• Pfadvariablen können in @Path doppelt vorkommen und müssen dann gleichen Wert bei Nutzung haben
• im Hinterkopf: wenn HTTPS, dann auch User-Token so übertrag- und später prüfbar (Sicherheit)
• im Hinterkopf: individueller Wert für jeden Nutzer, der E-Mail mit so einem Link erhält
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
426
Externer Service zur Analyse von IPs (1/4)
private static void zeigeJsonObjekt(JsonObject js){
for(String key:js.keySet()){
System.out.println(key+ ": " + js.get(key));
}
}
public static void main(String[] s){
String SERVICE = "http://freegeoip.net/json";
Client client = ClientBuilder.newClient();
WebTarget wt = client.target(SERVICE);
Invocation.Builder invoc = wt.request();
JsonObject ergebnis = invoc.get(JsonObject.class);
zeigeJsonObjekt(ergebnis);
zeigeJsonObjekt(client.target(SERVICE+"/www.bild.de")
.request().get(JsonObject.class));
}Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
427
Externer Service zur Analyse von IPs (2/4)
ip: "84.155.86.93"
country_code: "DE"
country_name: "Germany"
region_code: "NI"
region_name: "Lower Saxony"
city: "Neuenkirchen"
zip_code: "49586"
time_zone: "Europe/Berlin"
latitude: 52.4167
longitude: 7.85
metro_code: 0
ip: "72.247.9.43"
country_code: "US"
country_name: "United States"
region_code: "MA"
region_name: "Massachusetts"
city: "Cambridge"
zip_code: "02142"
time_zone: "America/New_York"
latitude: 42.3626
longitude: -71.0843
metro_code: 506
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
428
Externer Service zur Analyse von IPs (3/4)
public static void main(String[] st){
Client client = ClientBuilder.newClient();
WebTarget wt = client.target("http://freegeoip.net/json");
Invocation.Builder invoc = wt.request();
Response ergebnis = invoc.get();
System.out.println(ergebnis);
ergebnis.bufferEntity(); // sonst Fehler bei 42
System.out.println(ergebnis.getEntity());
for(String s:ergebnis.getHeaders().keySet()){
System.out.println(s +": " + ergebnis.getHeaders().get(s));
}
System.out.println(ergebnis.readEntity(JsonObject.class));
System.out.println(ergebnis.getEntity().getClass()); //42
ergebnis.close();
}Komponentenbasierte Software-
Entwicklung
Prof. Dr. Stephan Kleuker
429
Externer Service zur Analyse von IPs (4/4)
ScopedJaxrsResponse{ClientResponse{method=GET,
uri=http://freegeoip.net/json, status=200, reason=OK}}
java.io.ByteArrayInputStream@6d420a24
Date: [Wed, 14 May 2014 17:48:10 GMT]
Access-Control-Allow-Origin: [*]
Content-Length: [222]
Content-Type: [application/json]
{"ip":"93.196.192.46","country_code":"DE","country_name":"Germany","
region_code":"07","region_name":"Nordrhein-
Westfalen","city":"Hopsten","zipcode":"","latitude":52.3833,"longitu
de":7.6167,"metro_code":"","area_code":""}
class java.io.ByteArrayInputStream
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
430
Übergabe von Aufrufparametern (1/2)
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/rechnen")
public JsonObject machMathe(
@QueryParam("op1") int op1,
@QueryParam("op2") int op2,
@DefaultValue("plus")
@QueryParam("operator") String operator) {
JsonObjectBuilder erg = Json.createObjectBuilder();
if(operator.equals("minus")){
return erg.add("operator", operator)
.add("ergebnis", (op1-op2)).build();
}
return erg.add("operator", "plus")
.add("ergebnis", (op1+op2)).build();
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
431
Übergabe von Aufrufparametern (2/2)
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
432
Dienstnutzung mit Aufrufparametern (1/2)
public static void main(String[] s) {
String SERVICE
= "http://maps.googleapis.com/maps/api/geocode/json";
Client client = ClientBuilder.newClient();
WebTarget wt = client.target(SERVICE +
"?address=Quakenbrueck&sensor=false");
Invocation.Builder invoc = wt.request();
JsonObject ergebnis = invoc.get(JsonObject.class);
System.out.println(ergebnis);
JsonObject details = ((JsonArray)ergebnis.get("results"))
.getJsonObject(0);
JsonObject position= (JsonObject)
((JsonObject)details.get("geometry")).get("location");
System.out.println(position);
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
433
Dienstnutzung mit Aufrufparametern (2/2)
{"results":[{"address_components":[{"long_name":"Quakenbrück","
short_name":"Quakenbrück","types":["locality","political"]},{"l
ong_name":"Lower
Saxony","short_name":"NDS","types":["administrative_area_level_
1","political"]},{"long_name":"Germany","short_name":"DE","type
s":["country","political"]}],"formatted_address":"Quakenbrück,
Germany","geometry":{"bounds":{"northeast":{"lat":52.6967289,"l
ng":8.0344312},"southwest":{"lat":52.65917049999999,"lng":7.903
767999999999}},"location":{"lat":52.675599,"lng":7.950777699999
999},"location_type":"APPROXIMATE","viewport":{"northeast":{"la
t":52.6967289,"lng":8.0344312},"southwest":{"lat":52.6591704999
9999,"lng":7.903767999999999}}},"place_id":"ChIJqfqve3Zpt0cRIqf
jZXu8LGw","types":["locality","political"]}],"status":"OK"
{"lat":52.675599,"lng":7.950777699999999}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
434
UriInfo (1/2)
@Path("ana")
@Stateless
public class Analyse {
@Context
private UriInfo uriInfo;
private final static Logger LOGGER = Logger
.getLogger(Analyse.class.getSimpleName());
@GET
@Produces(MediaType.TEXT_PLAIN)
public String getText() {
LOGGER.info("in getText");
LOGGER.info(this.uriInfo.getAbsolutePath().toString());
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
435
UriInfo (2/2)
LOGGER.info(this.uriInfo.getPath());
LOGGER.info(this.uriInfo.getRequestUri().toString());
for (String s:this.uriInfo.getQueryParameters().keySet()){
LOGGER.info(s+ ": "
+ this.uriInfo.getQueryParameters().get(s));
}
return "hai";
}
INFO: in getText
INFO: http://localhost:8080/resources/ana
INFO: /ana
INFO: http://localhost:8080/resources/ana?x=Hai&text=42
INFO: text: [42]
INFO: x: [Hai]
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
436
Aufgabe
• Ergänze Mitarbeiterverwaltung um REST-Schnittstelle
• setze auf Basis-Architektur auf
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
437
Ergänzungen (1/3)
• Klassen zum Ergebnisaustausch JSON-fähig machen (Annotation oder von Hand)
• typischerweise toJsonObject(): JSsonObject, ggfls. auch Rückwandlung
• z. B. in MitarbeiterDTO:
public JsonObject toJSonObject() {
JsonObjectBuilder js = Json.createObjectBuilder();
js.add("minr", this.getMinr())
.add("vorname", this.getVorname())
.add("nachname", this.getNachname())
.add("alter", this.getAlter())
.add("befehl", this.befehl); // hier ignorieren
return js.build();
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
438
Ergänzungen (2/3)
• in Resultatpublic JsonObject toJSonObject() {
JsonObjectBuilder js = Json.createObjectBuilder();
js.add("ergebnis", this.isErgebnis())
.add("kommentar", this.getKommentar());
return js.build();
}
• REST einrichten (Pfad „r“ nur, fda er hier nicht leer sein soll, da zusammen mit JSF Applikation installiert, die bei Nutzung des einfachen Pfads aufgerufen wird)
@ApplicationPath("r")
public class ApplicationConfig extends Application { }
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
439
Ergänzungen (3/3)
• Methode wäre auch in MitarbeiterDTO sinnvoll
• hier in MitarbeiterRESTBoundary Null-Prüfung möglichprivate JsonObject toJSonObject(MitarbeiterDTO m) {
if (m == null) {
return null;
}
JsonObjectBuilder js = Json.createObjectBuilder();
js.add("minr", m.getMinr())
.add("vorname", m.getVorname())
.add("nachname", m.getNachname())
.add("alter", m.getAlter())
.add("link", uriInfo.getAbsolutePathBuilder()
.build().getPath());
return js.build();
}Komponentenbasierte Software-
Entwicklung
Prof. Dr. Stephan Kleuker
440
REST-Schnittstelle (1/5)
@Stateless // oder @Singleton
@Path("")
public class MitarbeiterRESTBoundary {
@Inject
private Controller controller;
@Context
private UriInfo uriInfo;
@GET
@Produces({MediaType.APPLICATION_JSON})
@Path("/mitarbeiter/{minr}")
public JsonObject mitarbeiter(@PathParam("minr") int minr) {
return this.toJSonObject(this.controller.mitarbeiter(minr));
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
441
REST-Schnittstelle (2/5)
@GET
@Produces({MediaType.APPLICATION_JSON})
@Path("/mitarbeiter")
public JsonObject alleMitarbeiter() {
List<MitarbeiterDTO> alle = this.controller.alleMitarbeiter();
JsonArrayBuilder elemente = Json.createArrayBuilder();
alle.forEach(m -> elemente.add(this.toJSonObject(m)));
return Json.createObjectBuilder()
.add("mitarbeiter", elemente)
.add("self", uriInfo.getAbsolutePathBuilder()
.path("/").build().getPath())
.build();
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
442
REST-Schnittstelle (3/5)
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Path("/mitarbeiter")
public JsonObject hinzufuegen(JsonObject jo) {
MitarbeiterDTO ma = new MitarbeiterDTO(0
,jo.getString("vorname")
, jo.getString("nachname"), "","");
return this.controller.save(ma).toJSonObject();
}
// hier wird das Protokoll fuer denb Nutzer festgelegt,
// es wird JSON-Objekt mit den angegebenen Attributen gefordert
// (Exception, wenn nicht vorhanden; besser behandeln)
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
443
REST-Schnittstelle (4/5)
@PUT
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Path("/mitarbeiter/{minr}")
public JsonObject aktualisieren(@PathParam("minr") int minr
, JsonObject jo) {
MitarbeiterDTO ma = this.controller.mitarbeiter(minr);
if (ma == null){
return (new Resultat(false, "kein Mitarbeiter mit minr "
+ minr)).toJSonObject();
}
ma.setVorname(jo.getString("vorname"));
ma.setNachname(jo.getString("nachname"));
return this.controller.update(ma).toJSonObject();
}Komponentenbasierte Software-
Entwicklung
Prof. Dr. Stephan Kleuker
444
REST-Schnittstelle (5/5)
@DELETE
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Path("/mitarbeiter/{minr}")
public JsonObject loeschen(@PathParam("minr") int minr) {
MitarbeiterDTO ma = this.controller.mitarbeiter(minr);
if (ma == null){
return (new Resultat(false
, "Mitarbeiter gelöscht (nicht existent)" + minr))
.toJSonObject();
}
return this.controller.delete(ma).toJSonObject();
}
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
445
Beispiel-Client (1/7)
• Client kann in beliebiger Sprache geschrieben sein
• Hier Beispiel mit Wiederverwendung, Frontend aus Mitarbeiterprojekt wird wieder genutzt, neue Controllerschicht, die REST nutzt
• Client schreibt eigene Mitarbeiterklasse (hier vereinfachend übernommen)
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
446
Beispiel-Client (2/7)
@Dependent
public class ControllerRESTFrontend implements Serializable {
private final static String MITARBEITER
= "http://localhost:8080/kbseMitarbeiterverwaltung"
+ "/r/mitarbeiter";
private Client client;
private WebTarget wt;
@PostConstruct
public void init() {
this.client = ClientBuilder.newClient();
this.wt = client.target(MITARBEITER);
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
447
Beispiel-Client (2/7)
@Dependent
public class ControllerRESTFrontend implements Serializable {
private final static String MITARBEITER
= "http://localhost:8080/kbseMitarbeiterverwaltung"
+ "/r/mitarbeiter";
private Client client;
private WebTarget wt;
@PostConstruct
public void init() {
this.client = ClientBuilder.newClient();
this.wt = client.target(MITARBEITER);
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
448
Beispiel-Client (3/7)public List<MitarbeiterDTO> alleMitarbeiter() {
Invocation.Builder build = this.wt
.request(MediaType.APPLICATION_JSON);
JsonObject ergebnis = build.get(JsonObject.class);
JsonArray array = ergebnis.getJsonArray("mitarbeiter");
List<MitarbeiterDTO> alle = new ArrayList<>();
for (JsonValue val : array) {
JsonObject js = (JsonObject) val;
MitarbeiterDTO tmp = new MitarbeiterDTO();
tmp.setAlter(js.getString("alter"));
tmp.setMinr(js.getInt("minr"));
tmp.setNachname(js.getString("nachname"));
tmp.setVorname(js.getString("vorname"));
alle.add(tmp);
}
return alle;
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
449
Beispiel-Client (4/7)
public String save(MitarbeiterDTO m) {
Invocation.Builder build = this.wt
.request(MediaType.APPLICATION_JSON);
Entity entity = Entity.entity(this.toJSonObject(m),
MediaType.APPLICATION_JSON);
try {
JsonObject ergebnis = build.post(entity
, JsonObject.class);
return ergebnis.getString("kommentar");
} catch (Exception e) {
return e.toString();
}
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
450
Beispiel-Client (5/7)
public String update(MitarbeiterDTO m) {
WebTarget wt = client.target(MITARBEITER + "/" + m.getMinr());
Invocation.Builder build = wt
.request(MediaType.APPLICATION_JSON);
Entity entity = Entity.entity(this.toJSonObject(m)
, MediaType.APPLICATION_JSON);
try {
JsonObject ergebnis = build.put(entity, JsonObject.class);
return ergebnis.getString("kommentar");
} catch (Exception e) {
return e.toString();
}
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
451
Beispiel-Client (6/7)
public String delete(MitarbeiterDTO m) {
WebTarget wt = client.target(MITARBEITER + "/" + m.getMinr());
Invocation.Builder build = wt
.request(MediaType.APPLICATION_JSON);
try {
JsonObject ergebnis = build.delete(JsonObject.class);
return ergebnis.getString("kommentar");
} catch (Exception e) {
return e.toString();
}
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
452
Beispiel-Client (7/7)
public MitarbeiterDTO mitarbeiter(int minr) {
WebTarget wt = client.target(MITARBEITER + "/" + minr);
Invocation.Builder build = wt
.request(MediaType.APPLICATION_JSON);
try {
JsonObject jo = build.get(JsonObject.class);
return MitarbeiterDTO.toObject(jo);
} catch (Exception e) {
return null;
}
}
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
453
Variante JavaScript-Client (1/10)
• JavaScript zentrale Sprache in den Browsern
• ursprünglich nur für kleine Aufgaben konzipiert (was man immer spürt)
• Standardisiert (ECMA-Script)
• Standard von keinem Browser konsequent eingehalten; es werden Browser-Switches benötigt
• Bibliotheken können Browser-Abhängigkeit teilweise wegkapseln
• viele sehr kreative, oft inkompatible (oft auch mit sich selbst in Versionssprüngen) Bibliotheken
• fast saubere Architektur möglich, leider selten
• hier nur sehr naiver Code ohne Tricks als Fallstudie
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
454
Variante JavaScript-Client (2/10)
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
455
Variante JavaScript-Client (3/10) – index.html (1/2)<!DOCTYPE html>
<html>
<head>
<title>REST Client für Mitarbeiter</title>
<meta http-equiv="Content-Type"
content="text/html; charset=UTF-8">
<script type="text/javascript" src="js/ws.js"></script>
</head>
<body>
<form name="felder">
<span id="pminr">Mitarbeiternummer wird vom
System vergeben</span>
<input id="minr" type="hidden">
<table border='0'>
<tr>
<td>Vorname:</td>
<td><input type="text" id="vorname"></td>
</tr>Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
456
Variante JavaScript-Client (4/10) – index.html (2/2)<tr>
<td>Nachname:</td>
<td><input type="text" id="nachname"></td>
</tr>
<tr>
<td><input type="button" value="uebernehmen"
onClick="sende();"> </td>
<td><input type="button" id="abbrechen"
value="abbrechen" onClick="abbruch();"
style=" display: none;"></td>
</tr>
</table>
<span id="kommentar"></span><br>
<div id="mitarbeiter" style="background-color: white;
margin:5px;">
</div>
</form>
</body>
</html>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
457
Variante JavaScript-Client (5/10) window.onload = function () {
laden();
}
PFAD =
"http://localhost:8080/kbseMitarbeiterverwaltung/r/mitarbeiter/";
function laden(){
var xhttp = new XMLHttpRequest();
xhttp.open("GET", PFAD, false);
xhttp.setRequestHeader("Content-Type", "application/json");
xhttp.send();
var response = JSON.parse(xhttp.responseText);
document.getElementById("mitarbeiter").innerHTML
= table(response.mitarbeiter);
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
458
Variante JavaScript-Client (6/10) function table(daten) {
data = daten;
var erg = "<table border='0'>";
erg += "<tr><th>Minr</th><th>Vorname</th><th>Nachname</th>";
erg += "<th>Alter</th><th></th></tr>";
for (var i = 0; i < data.length; i++) {
erg += "<tr>";
erg += "<td>" + data[i].minr + "</td>";
erg += "<td>" + data[i].vorname + "</td>";
erg += "<td>" + data[i].nachname + "</td>";
erg += "<td>" + data[i].alter + "</td>";
erg += "<td><input type='button' value='editieren' "
+ "onClick='edit(" + i + ");'></td>";
erg += "<td><input type='button' value='loeschen' "
+ "onClick='loeschen(" + i + ");'></td>";
erg += "</tr>";
}
erg += "</table>";
return erg;
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
459
Variante JavaScript-Client (7/10) function sende() {
var xhttp = new XMLHttpRequest();
var ma = {
vorname: document.getElementById('vorname').value,
nachname: document.getElementById('nachname').value,
minr: 0
};
// wenn „abbrechen“ sichtbar, dann ist es Aktualisierung
if (document.getElementById("abbrechen").style.display
=='block'){
ma.minr = parseInt(document.getElementById('minr').value, 10);
xhttp.open("PUT"
, PFAD + ma.minr
, false);
} else {
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
460
Variante JavaScript-Client (8/10) } else {
xhttp.open("POST"
, PFAD
, false);
}
xhttp.setRequestHeader("Content-Type", "application/json");
xhttp.send(JSON.stringify(ma));
var response = JSON.parse(xhttp.responseText);
document.getElementById("kommentar").innerHTML
= response.kommentar;
zuruecksetzen();
laden();
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
461
Variante JavaScript-Client (9/10) function abbruch() {
document.getElementById("abbrechen").style.display ='none';
zuruecksetzen();
}
function loeschen(i){
var xhttp = new XMLHttpRequest();
xhttp.open("DELETE"
, PFAD+data[i].minr
, false);
xhttp.setRequestHeader("Content-Type", "application/json");
xhttp.send();
var response = JSON.parse(xhttp.responseText);
laden();
document.getElementById("mitarbeiter").innerHTML
= table(response.mitarbeiter);
}Komponentenbasierte Software-
Entwicklung
Prof. Dr. Stephan Kleuker
462
Variante JavaScript-Client (10/10) function zuruecksetzen(){
document.getElementById('vorname').value = '';
document.getElementById('nachname').value = '';
document.getElementById("abbrechen").style.display ='none';
document.getElementById("pminr").innerHTML
='Mitarbeiternummer wird vom System vergeben';
}
function edit(i) {
document.getElementById("minr").value = data[i].minr;
document.getElementById("pminr").innerHTML
= "Mitarbeiternummer: " + data[i].minr;
document.getElementById("vorname").value = data[i].vorname;
document.getElementById("nachname").value = data[i].nachname;
document.getElementById("abbrechen").style.display ='block';
document.getElementById("kommentar").innerHTML ='';
}Komponentenbasierte Software-
Entwicklung
Prof. Dr. Stephan Kleuker
463
Vorherige Lösung ist schlechtes JavaScript
• Klasse XMLHttpRequest ist eine elementarer Baustein von Single-Page-Applications
• Nutzung erfolgt aktuell synchron, d. h. warten auf Ergebnis
• besser asynchron (asynchrones JavaScript und XML JSON)
• Projektaufbau davon unabhängig
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
464
Skizze – Umstellung auf asynchron (CallBack)
function laden(){
var xhttp = new XMLHttpRequest();
xhttp.open("GET"
, PFAD
, true); // true fuer asynchron
xhttp.setRequestHeader("Content-Type", "application/json");
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var response = JSON.parse(this.responseText);
document.getElementById("mitarbeiter").innerHTML
= table(response.mitarbeiter);
} // Fehlerbehandlung fuer anderen this.status sinnvoll
};
xhttp.send();
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
465
WADL (1/3)
• Web Application Description Language
• XML-basierte Beschreibung angebotener Dienste
• generell soll HTTP-Befehl OPTIONS genutzt werden, um Übersicht zu erhalten
• Alle möglichen Dienste mit Parametern werden aufgeführt
• Dienstbeschreibungen können aus Annotation generiert werden
• Alternativ kann @OPTIONS-annotierte Methode realisiert werden (z. B. um Ausgabe zu verhindern)
• Bedeutung eher gering, für Werkzeuge basierend auf WADL-Services interessant; erkennen so Aktualisierungen
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
466
WADL (2/3) - Beispielmethode
@GET
@Produces("text/html")
public String getHtml() {
return "<html><body>Hello, World!!</body></html>";
}
<resources base="http://localhost:8080/resources/">
<resource path="helloworld">
<method id="getHtml" name="GET">
<response>
<representation mediaType="text/html"/>
</response>
</method>
...
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
467
WADL (3/3) – Beispiel aus Sprinter
<resources base="http://localhost:8080/Sprinter/resources/">
<resource path="sprints">
<method id="getSprints" name="GET">
<request>
<param xmlns:xs="http://www.w3.org/2001/XMLSchema"
name="von"
style="query" type="xs:int" default="-1"/>
<param xmlns:xs="http://www.w3.org/2001/XMLSchema"
name="bis"
style="query" type="xs:int" default="-1"/>
</request>
<response>
<representation mediaType="application/json"/>
</response>
</method>
<method id="hinzufuegen" name="POST">
<request>
<representation mediaType="application/json"/>
</request>
<response>
<representation mediaType="application/json"/>
</response>
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
468
@FormParam
<form action="http://vfl.de/mitglieder" method="post">
<p>
Vorname: <input type="text" name="vorname"><br>
Nachname: <input type="text" name="nachname"><br>
<input type="submit" value="Send">
</p>
</form>
@Path("/mitglieder")
@Consumes(Mediatype.APPLICATION_FORM_URLENCODED)
public class CustomerResource {
@POST
public void createCustomer(
@FormParam(“vorname") String vorname
, @FormParam(“nachname") String nachname) {
...
}
ermöglicht die Übernahme von Parametern einer POST-Anfrage eines HTML-Formulars
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
469
Response.Status (gibt evtl. passende Exceptions)public enum Status {
OK(200, "OK"), CREATED(201, "Created"),
ACCEPTED(202, "Accepted"),
NO_CONTENT(204, "No Content"),
MOVED_PERMANENTLY(301, "Moved Permanently"),
SEE_OTHER(303, "See Other"),
NOT_MODIFIED(304, "Not Modified"),
TEMPORARY_REDIRECT(307, "Temporary Redirect"),
BAD_REQUEST(400, "Bad Request"),
UNAUTHORIZED(401, "Unauthorized"),
FORBIDDEN(403, "Forbidden"),
NOT_FOUND(404, "Not Found"),
NOT_ACCEPTABLE(406, "Not Acceptable"),
CONFLICT(409, "Conflict"), GONE(410, "Gone"),
PRECONDITION_FAILED(412, "Precondition Failed"),
UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"),
INTERNAL_SERVER_ERROR(500, "Internal Server Error"),
SERVICE_UNAVAILABLE(503, "Service Unavailable");
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
470
Weiterführend (1/2)
• asynchron@POST
@Asynchronous public void bearbeite(
@Suspended AsyncResponse ar, Daten daten)
• reguläre Ausdrücke in Path, @Path("{id : .+}")
komplexe Auswertungsregeln, was, wenn mehrere Möglichkeiten an Pfaden existieren
• HEAD: nimmt typischerweise erste GET und gibt statt Ergebnis nur Header und Response-Code zurück
• MIME-Types können sehr detailliert sein, generelltype/subtype;name=value;name=value...
@Consumes("application/xml;charset=utf-8")
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
471
Weiterführend (2/2)
• JAX-RS-Annotationen können auch nur in Interfaces ausgelagert werden
• Matrix-Parameter (Attribute) behandelbarhttp://beispiel.spieler.de/vfl;typ=Sturm/2015
• Nutzung von Header-Parametern @HeaderParampublic String get(@HeaderParam("Referrer") String
aufrufer) {
public String get(@Context HttpHeaders headers) {
• Cookie-Nutzung public String get(@CookieParam(“minr") int minr)
• genauere Analyse vom ResponseBuilder.status(.)
• Einbindung von Bean Validation
• …
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
472
Literatur
• (Standard-Links sind im Text)
• [Bur14] B. Burke, RESTful Java with JAX-RS 2.0, O‘Reilly, Sebastopol (CA), USA, 2014
• http://www.oracle.com/technetwork/articles/java/jaxrs20-1929352.html
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
473Komponentenbasierte Software-Entwicklung
6. Advanced JSF
6.1 Templates und Komponenten
6.2 Nutzung von Ajax
6.3 Testen von Web-Applikationen - Selenium
6.4 JSF-Erweiterungen
Prof. Dr. Stephan Kleuker
474Komponentenbasierte Software-Entwicklung
6.1 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
475Komponentenbasierte 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
476Komponentenbasierte 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
477Komponentenbasierte 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
478Komponentenbasierte 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
479Komponentenbasierte Software-Entwicklung
Erstes Template-Beispiel (5/5) - Nutzung
Prof. Dr. Stephan Kleuker
480Komponentenbasierte 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
481Komponentenbasierte 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
482Komponentenbasierte 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
483Komponentenbasierte 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
484
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
485Komponentenbasierte 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
486Komponentenbasierte 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
487Komponentenbasierte 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
488Komponentenbasierte 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
489Komponentenbasierte 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
490Komponentenbasierte Software-Entwicklung
Komponente (4/4) - Ausgabe, Projektstruktur
Ordner muss so heißen
Prof. Dr. Stephan Kleuker
491
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
492Komponentenbasierte 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
493Komponentenbasierte Software-Entwicklung
6.2 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
494Komponentenbasierte 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
495Komponentenbasierte 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
496Komponentenbasierte 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
497Komponentenbasierte 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
498
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
499Komponentenbasierte 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
500Komponentenbasierte 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
501Komponentenbasierte Software-Entwicklung
Interaktionsspielerei (3/4) – Ausgabe Firefox
Prof. Dr. Stephan Kleuker
502
Interaktionsspielerei (4/4) – Ausgabe Chrome
• Zähler laufen bei Bewegung auf dem Text los, maximaler Unterschied: eins
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
503Komponentenbasierte 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
504
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
505
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
506Komponentenbasierte Software-Entwicklung
Einschub: 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
507Komponentenbasierte 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
508Komponentenbasierte Software-Entwicklung
JSF und CSS (3/3) - Beispielausgabe
Prof. Dr. Stephan Kleuker
509
6.3 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
510
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
• nur automatisieren, wenn sinnvoll
• Ziel: Continuous Integration
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
511
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
512
Selenium WebDriver - Konfiguration
• unterstützt alle gängigen Browser
• bei neuen Browsern muss eventuell auf Aktualisierung von WebDriver gewartet werden
• Oft kleine zusätzliche Programme pro Browser benötigt
– Firefox: geckodriver (https://github.com/mozilla/geckodriver/releases) Proxy für Marionette
• über Nutzer-Profile nachdenken
• generell Browser-Version nutzen, der vor der genutzten Selenium-Version fertig war
• generell notwendig: eigene Testumgebung
• typisch: Testserver mit fester Konfiguration; nicht notwendigerweise neuestem Browser
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
513
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
514
Beispielprogramm (2/3)
Server starten
Applikation starten
http://localhost:8080/SecretSafe/
Komponentenbasierte Software-Entwicklung
vergebene Ids:
main:verfassen
main:lesen
Prof. Dr. Stephan Kleuker
515
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
516
Einblick in Nutzungsmöglichkeiten (1/14)public class SafeMoeglichkeitenTest {
private WebDriver driver;
private int linie = 0; // nur Ausgabespielerei
@BeforeClass
public static void setUpOnce() {
System.setProperty("webdriver.gecko.driver"
, "F:\\Programme\\geckodriver\\geckodriver.exe");
}
@Before
public void setUp() {
DesiredCapabilities capabilities = DesiredCapabilities.firefox();
capabilities.setCapability("marionette", true);
driver = new FirefoxDriver(capabilities);
// driver = new HtmlUnitDriver();
// driver = new ChromeDriver();
// driver = new InternetExplorerDriver(ieCapabilities);
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
517
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)
• Proxy zur Verbindung benötigt, FireFox: geckodriver.exe
• 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
518
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
519
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 Input-Element“)Komponentenbasierte Software-
Entwicklung
Prof. Dr. Stephan Kleuker
520
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
521
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
522
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)
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
523
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
524
Einblick in Nutzungsmöglichkeiten (9/14)(new WebDriverWait(driver, 10)).until(
new ExpectedCondition<WebElement>() {
@Override
public WebElement apply(WebDriver d) {
return d.findElement(By.id("eingabe"));
}
});
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
Warten bis Element erscheint
Prof. Dr. Stephan Kleuker
525
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 lbls = arguments[0]; "
+ "var inputs = []; "
+ "for (var i=0; i < lbls.length; i++){"
+ " inputs.push(document.getElementById("
+ " lbls[i].getAttribute('name'))); "
+ "} "
+ "return inputs;„ , labels);
zeigeElemente(inputs2);
Komponentenbasierte Software-Entwicklung
Aufrufparameter
Parameter für Aufruf
Prof. Dr. Stephan Kleuker
526
Einblick in Nutzungsmöglichkeiten (11/14)
• Ausgabe zur letzten Folie
----3form::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
527
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
528
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 aus org.apache,commons,io
// http://commons.apache.org/proper/commons-io/download_io.cgi
FileUtils.copyFile(screenshot,
new File("bild"+new Date().getTime()+".png"));
Assert.assertTrue(driver.getTitle().contains("Pssst"));
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
529
Einblick in Nutzungsmöglichkeiten (14/14)
• nach mehren Testläufen
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
530
Projektaufbau
• zentral benötigte Bibliotheken im Selenium-Download
Komponentenbasierte Software-Entwicklung
alle benötigt
benötigt (aktuelle
Variante)
Prof. Dr. Stephan Kleuker
531
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
532
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
533
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
534
Test des Beispiels (1/7)
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(); // zufaellige Texte
System.setProperty("webdriver.gecko.driver"
, "F:\\Programme\\geckodriver\\geckodriver.exe");
}
@Before
public void setUp() {
driver = new FirefoxDriver(profile);
}Komponentenbasierte Software-
Entwicklung
Prof. Dr. Stephan Kleuker
535
Test des Beispiels (2/7)@After
public void tearDown() {
driver.quit(); // dauert, sonst nur am Ende aller Tests
}
// zur Erkennung, ob Seite bereits geladen ist
private void warteAufSeiteMitId(String id) {
(new WebDriverWait(driver, 10)).until(
new ExpectedCondition<WebElement>() {
@Override
public WebElement apply(WebDriver d) {
return d.findElement(By.id(id));
}
});
}
private void startSeite(){
driver.get("http://localhost:8080/SecretSafe");
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
536
Test des Beispiels (3/7)
private void eingabeSeite(){
startSeite();
warteAufSeiteMitId("main");
driver.findElement(By.name("main:verfassen")).click();
warteAufSeiteMitId("eingabe");
}
private void ausgabeSeite(){
startSeite();
warteAufSeiteMitId("main");
driver.findElement(By.name("main:lesen")).click();
warteAufSeiteMitId("abfrage");
}
private void feldFuellen(String name, String wert){
driver.findElement(By.name(name)).clear();
driver.findElement(By.name(name)).sendKeys(wert);
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
537
Test des Beispiels (4/7)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();
warteAufSeiteMitId("erfolg");
Assert.assertTrue(driver.findElement(By.tagName("body"))
.getText().contains("Eintrag erfolgreich"));
}
private void textEingeben(String geheim, int versuche){
textEingeben(this.text1, this.text2, geheim, versuche);
}
Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
538
Test des Beispiels (5/7)
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();
warteAufSeiteMitId("antwort");
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();
warteAufSeiteMitId("antwort");
Assert.assertTrue(driver.findElement(By.tagName("body"))
.getText().contains("Treffen um 730 in KN2"));
}Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
539
Test des Beispiels (6/7)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
540
Test des Beispiels (7/7)
@Test
public void testLinkLesen(){
ausgabeSeite();
driver.findElement(By.linkText("Zur Startseite")).click();
warteAufSeiteMitId("main");
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();
warteAufSeiteMitId("main");
Assert.assertTrue(driver.findElement(By.tagName("body"))
.getText().contains("Was wollen Sie machen?"));
}
// weitere Tests sinnvoll
}Komponentenbasierte Software-Entwicklung
Prof. Dr. Stephan Kleuker
541
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
542Komponentenbasierte 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
543Komponentenbasierte Software-Entwicklung
6.4 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
544Komponentenbasierte Software-Entwicklung
Nutzung von PrimeFaces
• unterstützt JSF 2, Projekt ab November 2008
• Dokumentation war mal kostenpflichtig
• aktuell mit Support (Elite Downloads) und ohne (Community Downloads)
• Installation durch Einbinden einer einzelnen jar-Datei
• sehr viele Gestaltungsmöglichkeiten, mit Showcases gut dokumentiert
• Elemente haben oft sehr viele Parameter für Konfigurationsmöglichkeiten
• Ansatz: zu GUI-Element gehört Java-Objekt mit Konfigurationsmöglichkeiten in Java (set)
Prof. Dr. Stephan Kleuker
545Komponentenbasierte 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
546Komponentenbasierte 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
547Komponentenbasierte Software-Entwicklung
Beispiel: Editorspielerei (3/4) - h: (Seite lädt neu)
Prof. Dr. Stephan Kleuker
548Komponentenbasierte Software-Entwicklung
Beispiel: Editorspielerei (4/4) - p: (Aktualisierung)
Prof. Dr. Stephan Kleuker
549Komponentenbasierte 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://www.primefaces.org/documentation
Prof. Dr. Stephan Kleuker
550
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
551Komponentenbasierte 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