Page 1
Prof. Dr. Stephan Kleuker
569
6. Contexts and Dependency Injection
• klassisch Dependency Injection
• was kennen wir bereits
• CDI im Detail
– Aktivierung
– Qualifier
– Event Handling
– Objekte injizieren
– Produzenten
– Alternativen
– Interception
• Fazit
Komponentenbasierte Software-Entwicklung
Page 2
Prof. Dr. Stephan Kleuker
570
Einschub: aktueller Stand
• Sprinter ist eine klassisch aufgebaute JEE-Applikation
• wir arbeiten mit Klassen und Objekten davon
• kritisch betrachtet sind wesentliche Teile der Software nicht objektorientiert
– oftmals gibt es (pro Nutzer) genau ein Objekt von Steuerungsklassen (alles Singleton, Ausnahme Datenschicht)
– Teilprogramme rufen Methoden (?? Prozeduren) in anderen Teilprogrammen auf
• (Oracle Application Express (APEX) nutzt PL/SQL zurErstellung von Web-Applikationen)
• generell nichts Schlechtes daran!
• kritisch, Objekte müssen sich genau kennenKomponentenbasierte Software-
Entwicklung
Page 3
Prof. Dr. Stephan Kleuker
571
Ausblick auf weitere Themen
Komponentenbasierte Software-Entwicklung
Browser
Datenbank
JPA
EJBBean
Validation
CDIScope
JSFRESTful
WebService
Web Sockets
21
3
Page 4
Prof. Dr. Stephan Kleuker
572
Dependency Injection klassisch (1/2)
woher kommen Objekte für Exemplarvariablen?
• Variante 1: Werte werden als Parameter übergeben, aus denen Objekte gebaut werden (Objekt baut sich benötigte Objekte selber)
• Variante 2: Objekte werden als Referenzen übergeben
– Optimierung: Typen der Objektvariablen sind Interfaces; so konkrete Objekte leicht austauschbar
• Variante 2 heißt Dependency Injection mit get- und set-Methoden oder über Konstruktoren
Komponentenbasierte Software-Entwicklung
Page 5
Prof. Dr. Stephan Kleuker
573
Dependency Injection klassisch (2/2)
Nutzer nutzer = new Nutzer(new Inter1RealA(42)
, new Inter2RealC(43)
, new Inter3RealD("Hallo"));
eng verknüpft mit Factory Pattern
Komponentenbasierte Software-Entwicklung
Page 6
Prof. Dr. Stephan Kleuker
574
JSF nutzt bereits Dependency Injection
@Named
@SessionScoped
public class SprintController implements Serializable
@Inject
PersistenzService pers;
• @Named: Objekt steht und festen Namen zur Oberflächengestaltung zur Verfügung
• @SessionScoped: Objekt steht für die gesamte Session (auch allen anderen Objekten) zur Verfügung (Context)
• @Inject: „Umgebung gib (injiziere) mir ein zum Typen passendes Objekt“
• @PostConstruct: garantiert nach Erzeugung, vor Nutzung
• Hinweis: Annotationen stammen aus dem CDI-Paket; es gibt sehr ähnliche in JSF-Paketen (JSF ohne CDI machbar)
Komponentenbasierte Software-Entwicklung
Page 7
Prof. Dr. Stephan Kleuker
575
Ziele CDI
• Entkopplung von Objekten voneinander
– @Inject bestes Beispiel, es wird nur ein passendes Objekt „irgendwoher“ benötigt; dies besorgt CDI-Realisierung
• Vereinfachte Objekt-Kommunikation
– Beispiel: Informationen abonnieren (bekannt als PublishSubscribe oder Observer Observable)
• Vereinfachung von querschnittlich in mehreren Objekten benötigter Funktionalität
– Beispiel: Logging
• Hinzufügen von Funktionalität zu bestimmten Ereignissen ohne betroffene Methoden zu verändern
– Beispiel: Konsistenzprüfung, Benachrichtigung (Aspektorientierung)
Komponentenbasierte Software-Entwicklung
Page 8
Prof. Dr. Stephan Kleuker
576
Informationsquellen
• CDI 1.1 gehört zu JEE 7
• JSR 299: Contexts and Dependency Injection for the JavaTM EE platform, https://jcp.org/en/jsr/detail?id=299
• JSR 346: Contexts and Dependency Injection for JavaTM EE 1.1, https://jcp.org/en/jsr/detail?id=346
• JSR 365: Contexts and Dependency Injection for JavaTM 2.0 (Draft)
• Spezifikation http://www.cdi-spec.org/
– http://docs.jboss.org/cdi/spec/1.1/cdi-spec.pdf
– http://docs.jboss.org/cdi/spec/1.2/cdi-spec-1.2.pdf
• Referenzimplementierung Weld 2.0 (JBoss)
• gute Einführung: http://docs.jboss.org/weld/reference/latest/en-US/html/intro.html
Komponentenbasierte Software-Entwicklung
Page 9
Prof. Dr. Stephan Kleuker
577
NetBeans: kein Deploy on Save
gerade bei CDI ist ein vollständiges Clean & Build mit Deployfast immer sinnvoll, da Server sonst Probleme z. B. mit noch laufenden Sessions hat
Komponentenbasierte Software-Entwicklung
Page 10
Prof. Dr. Stephan Kleuker
578
Aktivierung
• explizit mit beans.xml in WEB-INF –Ordner; für reine EJB-Module oder jar-Dateien im Ordner META-INF<?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"
version="1.1" bean-discovery-mode="all">
</beans>
• implizit, ohne beans.xml oder mit und bean-discovery-mode="annotated", werden nur Beans mit Scope gefunden; typischerweise @Dependent für im Scope des Nutzers
• in beans.xml können (und müssen teilweise) weitere Eigenschaften spezifiziert werden
Komponentenbasierte Software-Entwicklung
Page 11
Prof. Dr. Stephan Kleuker
579
zentrales Hilfsmittel Qualifier
• Qualifier sind einfache Annotationen, mit denen gewünschte bzw. geforderte Eigenschaften spezifiziert werden können
• Normale neue Annotation mit Zusatzannotation @Qualifier...
import javax.inject.Qualifier;
@Qualifier
@Target({ElementType.TYPE, ElementType.METHOD,
ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Info{}
• folgende Folien: Qualifier nur erwähnt, haben dann die hier angegebene Form
Komponentenbasierte Software-Entwicklung
Page 12
Prof. Dr. Stephan Kleuker
580
Event-Model (Observer – Observable)
• auch Publish-Subscribe
• Observer sagt Bescheid, dass er vom Observable informiert werden möchte
• Observable schickt Informationen an alle Abonnenten
• Beispielaufgabe (Balkon): Informiere alle Interessierten, dass gerade ein Objekt persistiert werden soll
Komponentenbasierte Software-Entwicklung
Page 13
Prof. Dr. Stephan Kleuker
581
Event Model Übersicht (müssen nicht EJBs sein)
Komponentenbasierte Software-Entwicklung
class MeinEvent
beliebige POJO-Klasse
@Stateless
public class PersistenceService {
@Inject @Info Event<MeinEvent> event
…
MeinEvent me = …
event.fire(me);
Observable
@Qualifier
…
public @interface Info{}
Qualifier
@Stateless
public class EventConsumer {
public void empfangeMeinEvent(
@Observes @Info MeinEvent event)
{…
Observer
Page 14
Prof. Dr. Stephan Kleuker
582
Definition des Event-Objekts
• POJO mit Inhalten, die übertragen werden sollenpackage cdi.eventing;
public class MeinEvent { // POJO
private Object obj;
public MeinEvent(){
}
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
}
Komponentenbasierte Software-Entwicklung
Page 15
Prof. Dr. Stephan Kleuker
583
Observable – Benachrichtigen (Ausschnitt)
@Stateless
public class PersistenzService {
@Inject @Info Event<MeinEvent> event;
@Inject
private EntityManager em;
public void persist(Object object) {
MeinEvent e = new MeinEvent();
e.setObj(object);
event.fire(e);
em.persist(object);
}
Komponentenbasierte Software-Entwicklung
Page 16
Prof. Dr. Stephan Kleuker
584
Observer 1
• Durch Annotation @Info können verschiedenartige Events unterschieden werden
• beteiligte Objekte sind EJBs oder haben einen Scope (oder übernehmen Scope des nutzenden Objekts)@Stateless
public class EventConsumer {
public void empfangeMeinEvent(
@Observes @Info MeinEvent event) {
System.out.println(event.getObj());
}
}
// wenn Konsument nicht existiert, wird er erzeugt!
// nicht gewünscht: @Observes(notifyObserver=Recption.IF_EXISTS)
Komponentenbasierte Software-Entwicklung
Page 17
Prof. Dr. Stephan Kleuker
585
Observer 2
@Stateless
public class EventConsumer2 {
// Methode zeigt Machbarkeit des Ansatzes, ob dies hier
// sinnvoll, ist fraglich
public void empfangeMeinEvent(
@Observes @Info MeinEvent event) {
System.out.println(event.getObj().getClass());
if (event.getObj() instanceof Mitarbeit){
Mitarbeit m = (Mitarbeit)event.getObj();
if (m.getTaetigkeit().isEmpty()){
m.setTaetigkeit("intern");
}
}
}
}Komponentenbasierte Software-
Entwicklung
Page 18
Prof. Dr. Stephan Kleuker
586
Beispiel: neue Mitarbeit persistieren
INFO: Mitarbeit [id=0, version=0, geplanteStunden=8,
verbrauchteStunden=0, fertigstellungsgrad=0]
INFO: class entity.Mitarbeit
Komponentenbasierte Software-Entwicklung
Page 19
Prof. Dr. Stephan Kleuker
587
Konkretisierung injizierter Objekte
• bisher war immer eindeutig, welches Objekt bei @Inject genutzt wird
• häufig wird aber eine bestimmte Variante eines Objekts benötigt
• Ausgangspunkt: gibt Interface (oder abstrakte Klasse) mit mehreren Realisierungen
• der konkret gewünschte Objekttyp wird dann durch @Inject und die zusätzliche Angabe von Qualifiern festgelegt
• Beispiel: es gibt zwei verschiedene Ausgabemöglichkeiten, unterschieden durch Qualifier @LogQualifier und @SystemQualifier
Komponentenbasierte Software-Entwicklung
Page 20
Prof. Dr. Stephan Kleuker
588
Konkretisierung Übersicht
Komponentenbasierte Software-Entwicklung
interface Ausgabe {…
Angabe gewünschter Variante
@LogQualifier
public class AusgabeLog implements Ausgabe {…
@Qualifier
…
public @interface SystemQualifier{}
Qualifier
@Stateless
public class EventConsumer {
@Inject @LogQualifier Ausgabe aus;
@Qualifier
…
public @interface LogQualifier{}
Qualifier
@SystemQualifier
public class AusgabeSys implements Ausgabe {…
Interface
Realisierung
Realisierung
Page 21
Prof. Dr. Stephan Kleuker
589
Beispiel (1/4): Realisierungen 1/2
public interface Ausgabe {
public void ausgeben(String s);
}
@SystemQualifier
public class AusgabeSystem implements Ausgabe{
@Override
public void ausgeben(String s) {
System.out.println(s);
}
}
Komponentenbasierte Software-Entwicklung
Page 22
Prof. Dr. Stephan Kleuker
590
Beispiel (2/4): Realisierungen 2/2
@LogQualifier
public class AusgabeLog implements Ausgabe{
private final static Logger LOGGER = Logger
.getLogger(AusgabeLog.class.getSimpleName());
@Override
public void ausgeben(String s) {
LOGGER.log(Level.INFO, "AusgabeLog: {0}", s);
}
}
Komponentenbasierte Software-Entwicklung
Page 23
Prof. Dr. Stephan Kleuker
591
Beispiel (3/4): Auswahl der Realisierung
@Stateless
public class EventConsumer {
@Inject @LogQualifier Ausgabe aus;
public void empfangeMeinEvent(
@Observes @Info MeinEvent event) {
//System.out.println(event.getObj());
aus.ausgeben(event.getObj().toString());
}
}
• ein zentrales Logging kann man mit CDI besser durch Interceptors realisieren
• es können auch mehrere Qualifier angegeben werdenKomponentenbasierte Software-
Entwicklung
Page 24
Prof. Dr. Stephan Kleuker
592
Beispiel (4/4): Nutzung
INFO: AusgabeLog: (0) CDI einbauen
INFO: class entity.Sprint
Komponentenbasierte Software-Entwicklung
Page 25
Prof. Dr. Stephan Kleuker
593
Auswahlregeln
• Wenn Bean keinen Qualifier hat, wird er automatisch als @Default gesetzt (kann man auch hinschreiben)
• ohne benötigten Qualifier kann @Any genutzt werden
• Klassen haben ohne Scope-Angabe den Scope @Dependent, der sich dem Scope des nutzenden Objekts anpasst
Komponentenbasierte Software-Entwicklung
Page 26
Prof. Dr. Stephan Kleuker
594
Produzenten
• bisher wurde bei @Inject nach passenden Klassen gesucht und ein Objekt per Default-Konstruktor erzeugt
• Objekt-Erzeugung kann aber auch durch mit @Producesannotierte Konstruktoren, Methoden (Rückgabe-Objekt) oder direkt Exemplarvariablen erfolgen
• Konkretisierung des erzeugten Objekts wieder durch Qualifier (@Starttext, @Meldung im nächsten Beispiel)
• Beispiel zeigt kritische „Wiederverwendung“ von Qualifier
• javax.enterprise.inject.Produces nutzen
Komponentenbasierte Software-Entwicklung
Page 27
Prof. Dr. Stephan Kleuker
595
Statische Produzenten Übersicht (public vergessen)
Komponentenbasierte Software-Entwicklung
Produktions-varianten
@Qualifier
…
@interface Info{}
Qualifier
class Produzent {
@Produces @Meldung
private String meldung;
@Produces @Starttext
public String getLogtext() {…
@Produces @Starttext @Info
public String getLogtext2() {…
@Qualifier
…
@interface Meldung{}
Qualifier
class AusgabeLog {…
@Inject @Starttext @Info
private String start;
@Inject @Meldung
private String text;
…
Beispielnutzungen
@Qualifier
…
@interface Starttext{}
Qualifier
Page 28
Prof. Dr. Stephan Kleuker
596
Beispiel (1/3): Produzenten-Klasse
public class Produzent implements Serializable {
private String logtext = "logtext";
@Produces @Meldung
private String meldung;
public Produzent() { this.meldung = "Hai";}
@Produces @Starttext
public String getLogtext() {
return this.logtext;
}
@Produces @Starttext @Info
public String getLogtext2() {
System.out.println("getLogtext2");
return "2: " + this.logtext;
}
}
Komponentenbasierte Software-Entwicklung
Page 29
Prof. Dr. Stephan Kleuker
597
Beispiel (2/3): Nutzer der Produzenten
@LogQualifier
public class AusgabeLog implements Ausgabe{
private final static Logger LOGGER = Logger
.getLogger(AusgabeLog.class.getSimpleName());
@Inject @Starttext @Info private String start;
@Inject @Meldung private String text;
@Override
public void ausgeben(String s) {
//LOGGER.log(Level.INFO, "AusgabeLog: {0}", s);
LOGGER.log(Level.INFO, "{0} {1}: {2}"
, new Object[]{this.text, this.start, s});
}
}Komponentenbasierte Software-Entwicklung
Page 30
Prof. Dr. Stephan Kleuker
598
Beispiel (3/3): Beispielnutzung
INFO: getLogtext2
INFO: Hai 2: logtext: (0) Qualifier überlegen
INFO: class entity.BacklogElement
Komponentenbasierte Software-Entwicklung
Page 31
Prof. Dr. Stephan Kleuker
599
Dynamische Produzenten Übersicht
Komponentenbasierte Software-Entwicklung
Produktions-methode
Qualifier
public class Produzent{
@Produces @Aktuell
public String mach(){…
@Qualifier
…
public @interface Aktuell{}
public class AusgabeLog {
@Inject @Aktuell
Instance<String> datum;
…
public void ausgeben(…) {
…
datum.get()});
…
Beispielnutzung
Page 32
Prof. Dr. Stephan Kleuker
600
Dynamische Produktion (1/3) : Erzeugung
• wieder Produzenten-Methode mit üblichen Qualifier (also nichts Neues hier)
public class Produzent implements Serializable {
@Produces @Aktuell
public String mach(){
return new Date().toString();
}
...
Komponentenbasierte Software-Entwicklung
Page 33
Prof. Dr. Stephan Kleuker
601
Dynamische Produktion (2/3): Aufruf
@LogQualifier
public class AusgabeLog implements Ausgabe{
@Inject @Aktuell Instance<String> datum;
...
@Override
public void ausgeben(String s) {
LOGGER.log(Level.INFO, "AusgabeLog: {0} - {1}"
, new Object[]{s, datum.get()});
// LOGGER.log(Level.INFO, "{0} {1}: {2}"
// , new Object[]{this.text, this.start, s});
}
Komponentenbasierte Software-Entwicklung
Page 34
Prof. Dr. Stephan Kleuker
602
Dynamische Produktion (3/3): Beispielnutzung
INFO: AusgabeLog: (14) Fernando Alonso - Wed May 21 20:15:42 CEST 2014
INFO: AusgabeLog: (7) Kimi Räikkönen - Wed May 21 20:14:24 CEST 2014
Komponentenbasierte Software-Entwicklung
Page 35
Prof. Dr. Stephan Kleuker
603
Inject-Varianten
• statt @Inject an Exemplarvariablen zu schreiben, ist dies auch möglich:@Inject
public Konstruktor(Typ ichWerdeInjected){ …
@Inject
public void methode(Typ ichWerdeInjected){ …
• natürlich wieder Qualifier nutzbar
• weiterführend: mit @Typed an Klasse einschränken, für welche Klassen und Interfaces diese eingesetzt werden kann
Komponentenbasierte Software-Entwicklung
Page 36
Prof. Dr. Stephan Kleuker
604
Alternativen
• über Qualifier können passende Klassen genau ausgewählt werden
• z. B. zu Testzwecken, sollte diese Auswahl aber einfach änderbar sein
– hierzu wird Klasse mit @Alternative markiert
– UND muss in der beans.xml als ausgewählte Alternative angegeben werden
• durch Änderung der beans.xml sehr einfach Klassenauswahl auf Testphase oder länderspezifische Auswahlen änderbar
• mehrere Alternativen bei mehreren genutzten jars angebbar, dann Auswahl so steuerbar:
@Priority(Interceptor.Priority.APPLICATION + 10)
komplexes, sehr flexibles Auswahlsystem, wann welche Klasse genutzt wird (@Specialization)
Komponentenbasierte Software-Entwicklung
Page 37
Prof. Dr. Stephan Kleuker
605
Nutzung der Alternative (1/3): weitere Klasse
import cdi.qualifier.ausgabe.LogQualifier;
import javax.enterprise.inject.Alternative;
@Alternative
@LogQualifier
public class AusgabeLogMock implements Ausgabe{
@Override
public void ausgeben(String s) {
System.out.println("LogMock: " + s );
}
}
Komponentenbasierte Software-Entwicklung
Page 38
Prof. Dr. Stephan Kleuker
606
Nutzung der Alternative (2/3): bean.xml
<?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">
<alternatives>
<class>cdi.AusgabeLogMock</class>
</alternatives>
</beans>
Komponentenbasierte Software-Entwicklung
Page 39
Prof. Dr. Stephan Kleuker
607
Nutzung der Alternative (3/3): Nutzung
INFO: LogMock: (0) Alternativen realisieren
INFO: class entity.BacklogElement
Komponentenbasierte Software-Entwicklung
Page 40
Prof. Dr. Stephan Kleuker
608
Interceptor Übersicht
Komponentenbasierte Software-Entwicklung
unterbreche alle Methoden von
Interceptor-Benennung
@Stateless
@InterceptQualifier
public class BspKlasse {
public void meth1(…) {…
public void meth2(…) {…
…
@Inherited
@InterceptorBinding
…
public @interface InterceptQualifier{}
@InterceptQualifier
@Interceptor
public class MeinInterceptor {
@AroundInvoke
public Object logCall(
InvocationContext ctx)
throws Exception { …
Method meth = ctx.getMethod();
…
for (Object o: ctx.getParameters())
…
return ctx.proceed();
} …
bei Unterbrechung zu nutzen
<interceptors>
<class>cdi.MeinInterceptor
</class>
</interceptors>
Page 41
Prof. Dr. Stephan Kleuker
609
Interception
• Ansatz: sich in verschiedenen Methoden wiederholende Aufgaben zentral auslagern (Aspekt-Orientierung)
• (einziger) Klassiker: Logging
• Ansatz: Werden markierte Methoden oder Methoden in markierten Klassen ausgeführt, wird zunächst zum Interceptor gehörende Methode durchgeführt
• Interceptor kann auf Methode und Parameter zugreifen
• Interceptor muss Methodenausführung starten (proceed())
• Interceptor muss über bean.xml eingeschaltet werden (nur in diesem Archiv aktiv) oder @Priority-Annotation besitzen
• Interceptor benötigt eigene Art von Qualifier
• folgendes Beispiel zeigt ungewöhnliche Nutzung (auch Verstoß, dass möglichst wenig beobachtet werden soll)
Komponentenbasierte Software-Entwicklung
Page 42
Prof. Dr. Stephan Kleuker
610
Nutzung von Interception (1/5): Annotation
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.interceptor.InterceptorBinding;
//@Qualifier
@Inherited
@InterceptorBinding
@Target({ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface InterceptQualifier{}
Komponentenbasierte Software-Entwicklung
Page 43
Prof. Dr. Stephan Kleuker
611
Nutzung von Interception (2/5): Bereich festlegen
• Hier werden alle Methoden der Klasse beobachtet, man kann die Annotation auch nur für einzelne Methoden nutzen
@Stateless
@InterceptQualifier
public class EventConsumer2 {
public void empfangeMeinEvent(
@Observes @Info MeinEvent event) {
System.out.println(event.getObj().getClass());
if (event.getObj() instanceof Mitarbeit){
Mitarbeit m = (Mitarbeit)event.getObj();
if (m.getTaetigkeit().isEmpty()){
m.setTaetigkeit("intern");
}
}
}
}
Komponentenbasierte Software-Entwicklung
Page 44
Prof. Dr. Stephan Kleuker
612
Nutzung von Interception (3/5): Realisierung 1/2
@InterceptQualifier
@Interceptor
public class MeinInterceptor {
@AroundInvoke // gibt auch @AroundConstruct, @PostConstruct
public Object logCall(InvocationContext context)
throws Exception {
Method meth = context.getMethod();
System.out.println("Methode: " + meth);
/*
for (Object o : context.getParameters()) {
System.out.print(o + " ");
}
*/Komponentenbasierte Software-
Entwicklung
Page 45
Prof. Dr. Stephan Kleuker
613
Nutzung von Interception (4/5): Realisierung 2/2
MeinEvent event = (MeinEvent) context.getParameters()[0];
if (event.getObj() instanceof Mitarbeiter) {
Mitarbeiter m = (Mitarbeiter) event.getObj();
if (m.getMinr() == 999) {
m.setMinr((int) (100000 + System.nanoTime()%900000));
// m.setMinr((int) m.getId()); kann nicht gehen, da 0
}
}
return context.proceed(); // wichtig irgendwann aufrufen
}
}
Komponentenbasierte Software-Entwicklung
Page 46
Prof. Dr. Stephan Kleuker
614
Nutzung von Interception (3/4): beans.xml
<?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">
<interceptors>
<class>cdi.MeinInterceptor</class>
</interceptors>
</beans>
Komponentenbasierte Software-Entwicklung
Page 47
Prof. Dr. Stephan Kleuker
615
Nutzung von Interception (4/4): Nutzung
INFO: getLogtext2
INFO: Hai 2: logtext: (999) Clark Kent
INFO: Methode: public void
cdi.eventing.EventConsumer2.empfangeMeinEvent(cdi.eventing.
MeinEvent)
INFO: class entity.Mitarbeiter
Komponentenbasierte Software-Entwicklung
Page 48
Prof. Dr. Stephan Kleuker
616
Stereotype
• mit CDI können große Mengen von Annotationen entstehen
• häufiger haben ähnliche Klassen die gleichen Annotationen
• diese können als neue Annotation zusammengefasst werden
@RequestScoped
@Named
@MeineAnnotation
@Stereotype
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Aktion {}
• Klassen können auch mit mehreren Stereotypes (auch überlappend) annotiert werden
• Beispiel: @Model vereint @Named und @RequestScopedKomponentenbasierte Software-
Entwicklung
Page 49
Prof. Dr. Stephan Kleuker
617
Weiterführende Themen
• @Decorator, verwandt mit @Interceptor, ermöglicht Ergänzung von Funktionalität zu bestimmten Methoden
• CDI auch zu eigener Transaktionssteuerung nutzbar
Meinung: wenn JEE und EJB genutzt werden, spricht wenig für diesen Ansatz (Transaktion über mehrere Methoden)
interessant:
[Mül 14] B. Müller, JSF und JPA im Tandem, Teil 1, in: Javamagazin, 5/2014, Seiten 98-102, Software & Support Media GmbH, Frankfurt a. M. , 2014
• Auf Spezifikationsseite kann man sich über Version 1.2 informieren
Komponentenbasierte Software-Entwicklung
Page 50
Prof. Dr. Stephan Kleuker
618
Fazit
• CDI ermöglicht eine sehr große Entkopplung der Klassen voneinander
• Klassen werden so flexibler einsetzbar, evtl. Programmiermodel intuitiver
• im Beispiel wird „Balkon“ an Projekt programmiert, da @Inject und @...Scope ausreichen; nicht untypisch für klassisches JEE-Projekt
• CDI macht SW zur Zeit noch langsamer
• Annotations-Warfare-Area wird drastisch vergrößert
Komponentenbasierte Software-Entwicklung
Page 51
Prof. Dr. Stephan Kleuker
619
7. (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
Page 52
Prof. Dr. Stephan Kleuker
620
Ausblick auf weitere Themen
Komponentenbasierte Software-Entwicklung
Browser
Datenbank
JPA
EJBBean
Validation
CDIScope
JSFRESTful
WebService
Web Sockets
21
3
Page 53
Prof. Dr. Stephan Kleuker
621
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
Page 54
Prof. Dr. Stephan Kleuker
622
Vereinheitlichung von JSON in Java
in JEE 7 ergänzt:
• JSR 353: JavaTM API for JSON Processing (23.5.2013), https://jcp.org/en/jsr/detail?id=353
• 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
Page 55
Prof. Dr. Stephan Kleuker
623
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
Page 56
Prof. Dr. Stephan Kleuker
624
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
Page 57
Prof. Dr. Stephan Kleuker
625
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
Page 58
Prof. Dr. Stephan Kleuker
626
Ausschnitt Klassendiagramm
Komponentenbasierte Software-Entwicklung
Page 59
Prof. Dr. Stephan Kleuker
627
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
Page 60
Prof. Dr. Stephan Kleuker
628
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
Page 61
Prof. Dr. Stephan Kleuker
629
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
Page 62
Prof. Dr. Stephan Kleuker
630
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
Page 63
Prof. Dr. Stephan Kleuker
631
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
Page 64
Prof. Dr. Stephan Kleuker
632
Hintergrund: Service Oriented Architecture
Service-
Verzeichnis
Service-
Anbieter
Service-
Nutzer
3. Anfragen
4. Antworten
SOAP
WSDL
HTTP
UDDI
Komponentenbasierte Software-Entwicklung
Page 65
Prof. Dr. Stephan Kleuker
633
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
Page 66
Prof. Dr. Stephan Kleuker
634
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
Page 67
Prof. Dr. Stephan Kleuker
635
Woher kommt „ Representational State Transfer“
Client fordert Information mit Hilfe einer URL an.
Eine Repräsentation der Information wird als Ergebnis zurückgegeben (z. B. in Form eines JSON-Objekts), Client hat Informationszustand.
Client nutzt Hyperlink 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
Page 68
Prof. Dr. Stephan Kleuker
636
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)
• Der vollständige Informationszustand kann beim Client oder beim Server liegen, oder auch über beide verteilt sein
• HTTP-Kommunikationsprotokoll selbst bleibt zustandslos
• Grundregel: GET, PUT, DELETE 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
Page 69
Prof. Dr. Stephan Kleuker
637
Wer nutzt es (Beispiele)?
• Hinweis: Öfter wird gegen die geforderte Reinform von RESTful WebServices verstoßen, und normale Anfragemöglichkeit mit GET als RESTful bezeichnet
• Google Maps
• Google AJAX Search API
• Yahoo Search API
• Amazon WebServices
Komponentenbasierte Software-Entwicklung
Page 70
Prof. Dr. Stephan Kleuker
638
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: The JavaTM API for RESTful Web Services, https://jcp.org/en/jsr/detail?id=311 (10.10.2008)
• JSR 339: JAX-RS 2.0: The Java API for RESTful Web Services, https://jcp.org/en/jsr/detail?id=339 (24.5.2013)
Komponentenbasierte Software-Entwicklung
Page 71
Prof. Dr. Stephan Kleuker
639
JAX-RS aktivieren
• in JEE-aware Servern reicht theoretisch folgendes ausimport javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
@ApplicationPath("resources")
public class ApplicationConfig extends Application {
}
• ist generell im .war-File
• sonst Konfiguration als Servlet nötig
• Beschreibung in web.xml
Komponentenbasierte Software-Entwicklung
Page 72
Prof. Dr. Stephan Kleuker
640
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
Page 73
Prof. Dr. Stephan Kleuker
641
erste RESTful-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
Page 74
Prof. Dr. Stephan Kleuker
642
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
Page 75
Prof. Dr. Stephan Kleuker
643
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
Page 76
Prof. Dr. Stephan Kleuker
644
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
Komponentenbasierte Software-Entwicklung
Page 77
Prof. Dr. Stephan Kleuker
645
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
Page 78
Prof. Dr. Stephan Kleuker
646
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
Page 79
Prof. Dr. Stephan Kleuker
647
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
Page 80
Prof. Dr. Stephan Kleuker
648
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
Page 81
Prof. Dr. Stephan Kleuker
649
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
Page 82
Prof. Dr. Stephan Kleuker
650
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
Page 83
Prof. Dr. Stephan Kleuker
651
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
Page 84
Prof. Dr. Stephan Kleuker
652
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
Page 85
Prof. Dr. Stephan Kleuker
653
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
Page 86
Prof. Dr. Stephan Kleuker
654
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
Page 87
Prof. Dr. Stephan Kleuker
655
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
Page 88
Prof. Dr. Stephan Kleuker
656
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
Page 89
Prof. Dr. Stephan Kleuker
657
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
Page 90
Prof. Dr. Stephan Kleuker
658
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
Page 91
Prof. Dr. Stephan Kleuker
659
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
Page 92
Prof. Dr. Stephan Kleuker
660
Beispiel: Umsetzung von Pfaden (4/4)
{"fehler":"kein Kunde"}
{"fehler":"kein Kunde"}
{"summe":32000000}
{"fehler":"kein Konto"}
Komponentenbasierte Software-Entwicklung
Page 93
Prof. Dr. Stephan Kleuker
661
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
Page 94
Prof. Dr. Stephan Kleuker
662
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
Page 95
Prof. Dr. Stephan Kleuker
663
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
Page 96
Prof. Dr. Stephan Kleuker
664
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
Page 97
Prof. Dr. Stephan Kleuker
665
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
Page 98
Prof. Dr. Stephan Kleuker
666
Ü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
Page 99
Prof. Dr. Stephan Kleuker
667
Übergabe von Aufrufparametern (2/2)
Komponentenbasierte Software-Entwicklung
Page 100
Prof. Dr. Stephan Kleuker
668
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
Page 101
Prof. Dr. Stephan Kleuker
669
Dienstnutzung mit Aufrufparametern (2/2)
{"results":[{"address_components":[{"long_name":"Quakenbrück","
short_name":"Quakenbrück","types":["locality","political"]},{"l
ong_name":"Osnabrück","short_name":"OS","types":["administrativ
e_area_level_3","political"]},{"long_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}}},"types":["locality","political"
]}],"status":"OK"}
{"lat":52.675599,"lng":7.950777699999999}
Komponentenbasierte Software-Entwicklung
Page 102
Prof. Dr. Stephan Kleuker
670
Aufgabe
Sprinter soll um eine RESTful-Schnittstelle ergänzt werden,
• mit der von außen auf Sprints zugegriffen werden kann,
• die nur eine Teilmenge der Daten der Sprints sieht,
• die neue Sprints anlegen kann,
• die Sprints editieren kann,
• die Sprints löschen kann
• Entscheidung: Ergänze Programm um RESTful Webservices
• Schnittstelle wird in neuem Projekt genutzt (das zum einfacheren Verständnis eine JSF-Oberfläche bekommt)
Komponentenbasierte Software-Entwicklung
Page 103
Prof. Dr. Stephan Kleuker
671
Nutzungsszenario
• Links nicht ausimplementiert
Komponentenbasierte Software-Entwicklung
Page 104
Prof. Dr. Stephan Kleuker
672
Architektur: hierarchischer Aufbau
Resource POST(CREATE)
GET(READ)
PUT(UPDATE)
DELETE(DELETE)
/sprints erzeugt neuen Sprint
Übersicht über alle Sprints
Aktualisiere alle Sprints (oder weglassen)
alle Sprints löschen
/sprints/42 Fehler! Zeige Sprint mit id 42
wenn Sprint mit id 42 existiert, dann aktualisieren, (sonst Fehler ?)
Lösche den Sprint mit id42
Hinweise: noch sauberer wäre /sprint/42 (Einzahl)graue Felder nicht realisiert
Komponentenbasierte Software-Entwicklung
Page 105
Prof. Dr. Stephan Kleuker
673
Einordnung SprintRestController (Server)
Komponentenbasierte Software-Entwicklung
Page 106
Prof. Dr. Stephan Kleuker
674
Client (minimal)
Komponentenbasierte Software-Entwicklung SprinterRESTClient
Page 107
Prof. Dr. Stephan Kleuker
675
Vorbereitung im Server
@Stateless // oder @Singleton
@Path("")
public class SprintRestController implements Serializable{
@Inject
private PersistenzService pers;
@Context
private UriInfo uriInfo; // später genauer
private SimpleDateFormat formatter
= new SimpleDateFormat("dd.MM.yyyy");
public SprintRestController() {
}
Komponentenbasierte Software-Entwicklung
Page 108
Prof. Dr. Stephan Kleuker
676
Hilfsmethode zum Sprint einpacken
private JsonObject jsonSprint(Sprint s, boolean einzeln) {
String idzeigen = (einzeln) ? "" : "" + s.getId();
JsonObjectBuilder js = Json.createObjectBuilder();
js.add("id", s.getId())
.add("motto", s.getMotto())
.add("starttermin", formatter.format(s.getStarttermin()))
.add("endtermin", formatter.format(s.getEndtermin()))
.add("geplanterAufwand", s.getGeplanterAufwand())
.add("farbe", s.color())
.add("link", uriInfo.getAbsolutePathBuilder()
.path(idzeigen + "/backlogElemente")
.build().getPath());
return js.build();
}
Komponentenbasierte Software-Entwicklung
Page 109
Prof. Dr. Stephan Kleuker
677
GET /sprints (1/2)
@GET
@Produces({MediaType.APPLICATION_JSON})
@Path("/sprints")
public JsonObject getSprints(
@DefaultValue("-1") @QueryParam("von") int von,
@DefaultValue("-1") @QueryParam("bis") int bis) {
List<Sprint> alle = pers.findAllSprint();
if (von < 0 || von >= alle.size()) {
von = 0;
}
if (bis < 0 || bis >= alle.size()) {
bis = alle.size() - 1;
}
Komponentenbasierte Software-Entwicklung
Page 110
Prof. Dr. Stephan Kleuker
678
GET /sprints (2/2)
JsonArrayBuilder elemente = Json.createArrayBuilder();
for (int i = von; i <= bis; i++) {
elemente.add(jsonSprint(alle.get(i), false));
}
return Json.createObjectBuilder()
.add("sprints", elemente)
.build();
}
Komponentenbasierte Software-Entwicklung
Page 111
Prof. Dr. Stephan Kleuker
679
Client – Vorbereitung (1/2)
• Client braucht keine echte Datenhaltung
• Ansatz: Daten lokal in SessionScope halten (für kleinere Datenmengen ok
@Named
@SessionScoped
public class SprintController implements Serializable {
private Client client;
private List<Map<String, Object>> sprints;
private final static String[] keys = {"id", "motto"
, "starttermin", "endtermin", "geplanterAufwand"
, "link", "farbe"};
private final static String SPRINTS
= "http://localhost:8080/Sprinter/resources/sprints";
private final static String HOME = "index";
Komponentenbasierte Software-Entwicklung
Page 112
Prof. Dr. Stephan Kleuker
680
Client – Vorbereitung (2/2)
• eine Variable pro Eigenschaft mit get und setenum Status {BASIC, EDIT;}
private long id;
private String motto;
private Date starttermin;
private Date endtermin;
private int geplanterAufwand;
private Status modus;
private String meldung = ""; // Statusmeldung ohne Voodoo
SimpleDateFormat formatter
= new SimpleDateFormat("dd.MM.yyyy");
public SprintController() {
}
Komponentenbasierte Software-Entwicklung
Page 113
Prof. Dr. Stephan Kleuker
681
Client – Initialisierung (1/2)
@PostConstruct
public void init() {
this.modus = Status.BASIC;
this.client = ClientBuilder.newClient();
WebTarget wt = client.target(SPRINTS);
Invocation.Builder buil = wt
.request(MediaType.APPLICATION_JSON);
JsonObject ergebnis = buil.get(JsonObject.class);
JsonArray array = ergebnis.getJsonArray("sprints");
this.sprints = new ArrayList<Map<String, Object>>();
for (JsonValue val : array) {
JsonObject js = (JsonObject) val;
// speichert einen String als Attribut/Wert-Paar
Map<String, Object> werte = new HashMap<String, Object>();
Komponentenbasierte Software-Entwicklung
Page 114
Prof. Dr. Stephan Kleuker
682
Client – Initialisierung (2/2)
for (String k : keys) {
werte.put(k, js.get(k));
}
this.sprints.add(werte);
}
this.motto = "";
this.starttermin = null;
this.endtermin = null;
this.geplanterAufwand = 0;
} in älteren Versionen überflüssige " entfernen for (String k : keys) {
Object tmp = js.get(k);
String txt = tmp.toString();
if(txt.startsWith("\"") && txt.endsWith("\"") && txt.length() > 1){
tmp = txt.substring(1, txt.length()-1);
}
werte.put(k, tmp);
Komponentenbasierte Software-Entwicklung
Page 115
Prof. Dr. Stephan Kleuker
683
Erfolgloses Löschen möglich
• vom anderen Nutzer gelöscht oder modifiziert
• Idempotent wäre, diesen Fehler zu ignorieren (ist gelöscht)
Komponentenbasierte Software-Entwicklung
Page 116
Prof. Dr. Stephan Kleuker
684
Server Action loeschen
@DELETE
@Path("/sprints/{id}")
public JsonObject loeschen(@PathParam("id") long id) {
pers.removeSprint(id);
JsonObjectBuilder js = Json.createObjectBuilder();
js.add("status", "geloescht");
return js.build();
}
Komponentenbasierte Software-Entwicklung
Page 117
Prof. Dr. Stephan Kleuker
685
Client löschen
public String loeschen(Object sid) {
System.out.println("loeschen: " + sid);
WebTarget wb = client.target(SPRINTS + "/" + sid);
Invocation.Builder build = wb
.request(MediaType.APPLICATION_JSON);
try {
JsonObject ergebnis = build.delete(JsonObject.class);
this.meldung = "loeschen erfolgreich: " + ergebnis;
} catch (Exception e) {
this.meldung = "loeschen gescheitert: " + e;
}
init();
this.modus = Status.BASIC;
return HOME;
}Komponentenbasierte Software-
Entwicklung
Page 118
Prof. Dr. Stephan Kleuker
686
Neuer Sprint – Server (1/2)
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Path("/sprints")
public JsonObject hinzufuegen(JsonObject jo) {
Sprint sprint = new Sprint();
sprint.setMotto(jo.getString("motto"));
sprint.setGeplanterAufwand(jo.getInt("geplanterAufwand"));
SimpleDateFormat formatter
= new SimpleDateFormat("dd.MM.yyyy");
try {
sprint.setStarttermin(formatter
.parse(jo.getString("starttermin")));
Komponentenbasierte Software-Entwicklung
Page 119
Prof. Dr. Stephan Kleuker
687
Neuer Sprint – Server (2/2)
sprint.setEndtermin(formatter
.parse(jo.getString("endtermin")));
} catch (ParseException ex) {
return null;
}
pers.persist(sprint);
JsonObjectBuilder js = Json.createObjectBuilder();
js.add("link"
, uriInfo.getAbsolutePathBuilder()
.path(sprint.getId() + "/backlogElemente")
.build().getPath());
return js.build();
}
Komponentenbasierte Software-Entwicklung
Page 120
Prof. Dr. Stephan Kleuker
688
Neuer Sprint – Client Aktion uebernehmen (1/4)
public String uebernehmen() {
// Validerung des Clients muss dieser regeln
if (this.starttermin == null || this.endtermin == null){
this.meldung = "Start- und Endtermin angeben!";
return HOME;
}
if (this.starttermin.compareTo(this.endtermin) > 0){
this.meldung = "Endtermin nicht vor Starttermin";
return HOME;
}
Komponentenbasierte Software-Entwicklung
Page 121
Prof. Dr. Stephan Kleuker
689
Neuer Sprint – Client Aktion uebernehmen (2/4)
JsonObjectBuilder js = Json.createObjectBuilder();
js.add("motto", this.motto)
.add("starttermin", formatter.format(this.starttermin))
.add("endtermin", formatter.format(this.endtermin))
.add("geplanterAufwand", this.geplanterAufwand);
if (this.modus.equals(Status.BASIC)) {
neuerSprint(js);
}
if (this.modus.equals(Status.EDIT)) {
editiereSprint(js);
}
init();
this.modus = Status.BASIC;
return HOME;
}Komponentenbasierte Software-
Entwicklung
Page 122
Prof. Dr. Stephan Kleuker
690
Neuer Sprint – Client Aktion uebernehmen (3/4)
private void neuerSprint(JsonObjectBuilder js){
WebTarget wb = client.target(SPRINTS);
Invocation.Builder build = wb
.request(MediaType.APPLICATION_JSON);
Entity entity = Entity.entity(js.build()
, MediaType.APPLICATION_JSON);
try {
JsonObject ergebnis = build.post(entity, JsonObject.class);
this.meldung = "einfuegen erfolgreich: " + ergebnis;
} catch (Exception e) {
this.meldung = "einfuegen gescheitert: " + e;
}
}
Komponentenbasierte Software-Entwicklung
Page 123
Prof. Dr. Stephan Kleuker
691
Sprint editieren – Server (1/2)
@PUT
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Path("/sprints/{id}")
public JsonObject aktualisieren( @PathParam("id") long id
, JsonObject jo) {
Sprint sprint = pers.findSprint(id);
sprint.setMotto(jo.getString("motto"));
sprint.setGeplanterAufwand(jo.getInt("geplanterAufwand"));
try {
sprint.setStarttermin(formatter
.parse(jo.getString("starttermin")));
sprint.setEndtermin(formatter
.parse(jo.getString("endtermin")));
Komponentenbasierte Software-Entwicklung
Page 124
Prof. Dr. Stephan Kleuker
692
Sprint editieren – Server (2/2)
} catch (ParseException ex) {
return null;
}
pers.merge(sprint);
JsonObjectBuilder js = Json.createObjectBuilder();
js.add("link"
, uriInfo.getAbsolutePathBuilder()
.path("/backlogElemente").build().getPath());
return js.build();
}
Komponentenbasierte Software-Entwicklung
Page 125
Prof. Dr. Stephan Kleuker
693
Editiere Sprint – Client Aktion uebernehmen (4/4)
private void editiereSprint(JsonObjectBuilder js) {
WebTarget wb = client.target(SPRINTS + "/" + this.id);
Invocation.Builder build = wb
.request(MediaType.APPLICATION_JSON);
js.add("id", id);
Entity entity = Entity.entity(js.build()
, MediaType.APPLICATION_JSON);
try {
JsonObject ergebnis = build.put(entity, JsonObject.class);
this.meldung = "aktualisieren erfolgreich: " + ergebnis;
} catch (Exception e) {
this.meldung = "aktualisieren gescheitert: " + e;
}
}
Komponentenbasierte Software-Entwicklung
Page 126
Prof. Dr. Stephan Kleuker
694
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
Page 127
Prof. Dr. Stephan Kleuker
695
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
Page 128
Prof. Dr. Stephan Kleuker
696
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
Page 129
Prof. Dr. Stephan Kleuker
697
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
Page 130
Prof. Dr. Stephan Kleuker
698
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
Page 131
Prof. Dr. Stephan Kleuker
699
@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
Page 132
Prof. Dr. Stephan Kleuker
700
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
Page 133
Prof. Dr. Stephan Kleuker
701
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
Page 134
Prof. Dr. Stephan Kleuker
702
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
Page 135
Prof. Dr. Stephan Kleuker
703
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
Page 136
Prof. Dr. Stephan Kleuker
704
8. WebSockets
• WebSockets
– Verbreitung
– zentrale Nachrichten
– Realisierung eines Chats
– Encoder
– Decoder
• Bedeutung von JavaScript
basiert teilweise auf: [Dit14] A. Ditler, Prototypische Realisierung eines Echtzeit-Webchats als Crossplattform-Applikation auf Basis von Websockets, Hochschule Osnabrück, Bachelorarbeit, 2014
Komponentenbasierte Software-Entwicklung
Page 137
Prof. Dr. Stephan Kleuker
705
WebSockets - Motivation
• HTTP erlaubt nur die Beantwortung von Client-Anfragen
• ohne Erweiterung keine Möglichkeit, dass der Server den Client nachträglich ohne erneute Anfrage informiert
• nur mit Workaround z. B. AJAX und Long-Polling möglich
• WebSockets erlauben die bidirektionale Kommunikation zwischen Client und Server
• allgemein: The WebSocket API, W3C Candidate Recommendation, 20.09.2012, http://www.w3.org/TR/websockets/
• standardisiert in Java: JSR 356: JavaTM API for WebSocket, https://jcp.org/en/jsr/detail?id=356
Komponentenbasierte Software-Entwicklung
Page 138
Prof. Dr. Stephan Kleuker
706
Verbreitung (1/2)
• aktuell wird Ansatz in vielen Projekten evaluiert, Nutzung hängt von Zielplattformen ab
• Anmerkung: Abkündigung von Win XP-Support lässt alte IE-Browser verschwinden
• genauer müssen unterstütze Prokollversionen (ab wann) und Zielplattformen zusammen evaluiert werden
• Beispiel: Android-Browser erst ab Android 4.4, andere Browser für Android schon eher
• Beispiel: Web-Seite soll auch zur App auf Handys werden, ein Ansatz mit Apache Cordova / Phonegap, unterstützt nur etwas ältere WebSocket-Version
Komponentenbasierte Software-Entwicklung
Page 139
Prof. Dr. Stephan Kleuker
707
Verbreitung (2/2)
aktueller Stand: http://caniuse.com/websockets
• IE ab 10.0, Edge ab Start
• Firefox ab 11.0
• Chrome ab 16
• Safari ab 7.0
• Opera ab 12.1
• iOS Safari ab 6.1
• Android Browser ab 4.4
• Blackberry Browser ab 7.0
• Chrome for Android ab 33.0
• Firefox for Android ab 26
• IE Mobile ab 10.0
Komponentenbasierte Software-Entwicklung
Page 140
Prof. Dr. Stephan Kleuker
708
grobe Funktionsweise / Potenzial
• Client und Server in Java möglich, genauso gut können aber Clients in anderen Sprachen geschrieben werden (Server auch)
• Verbindung mit Server wird über HTTP hergestellt, Server dabei auf Upgrade auf WebSocket-Protokoll befragt
typische Adresse: new URI("ws://localhost:1790/hallo/echo")
• wenn Server Upgrade anbietet, wird bidirektional nutzbare Verbindung aufgebaut (ohne dass diese physikalisch gehalten werden muss)
• wenn Server kein Upgrade anbietet, ist Ansatz gescheitert
• es gibt verschiedene Protokoll-Versionen, auch hier muss sich auf eine geeinigt werden
Komponentenbasierte Software-Entwicklung
Page 141
Prof. Dr. Stephan Kleuker
709
etwas Hintergrund
• Auf TCP basierendes Netzwerkprotokoll
• Bidirektionale – Vollduplex Kommunikation
• Verbindung basiert auf einem einzigen Socket
• Datenübertragung mit geringer Latenzzeit
• Datenaustausch: binär, utf-8, …, nur Zeichenketten oder Byte-Buffer
• Websocket-Verbindung: ws:// und wss://
• keine Probleme bei Firewalls und Proxy-Servern
• Referenzimplementierung: Tyrus https://tyrus.java.net/
• Alternativen: GNU WebSocket4J, Webbit, Tootallnate
Komponentenbasierte Software-Entwicklung
Page 142
Prof. Dr. Stephan Kleuker
710
zentrale Nachrichten
Server Client
onOpen Verbindungsaufbau onOpen
onMessage senden und empfangen onMessage
onClose Verbindungsabbau onClose
onError Fehlerfall onError
eigentliches Protokoll, was in welcher Form ausgetauscht wird, muss von Entwicklern festgelegt werden
Begriff „Socket“ kann ernst genommen werden
Komponentenbasierte Software-Entwicklung
Page 143
Prof. Dr. Stephan Kleuker
711
Erstes Beispiel (1/7): Gewünscht
Client Server
connectToServer
@OnOpen (Bestätigung)
send(Hello) @OnMessage
@OnMessage send(Hallo Client)
send(Hello again) @OnMessage
@OnMessage send(Hallo Client)
(schließen) @OnClose
@OnClose (Bestätigung)
Komponentenbasierte Software-Entwicklung
Page 144
Prof. Dr. Stephan Kleuker
712
Erstes Beispiel (2/7): Server (1/2)
@ServerEndpoint("/echo")
public class EchoServer {
@OnOpen
public void onOpen(Session session, EndpointConfig cfg) {
System.out.println("@Server Anfrage URI: "
+ session.getRequestURI());
}
@OnMessage
public void onMessage(String message, Session session)
throws IOException {
System.out.println("@Server Nachricht: " + message);
session.getBasicRemote().sendText("Hallo Client");
}
Komponentenbasierte Software-Entwicklung
Page 145
Prof. Dr. Stephan Kleuker
713
Erstes Beispiel (3/7): Server (2/2)
@OnClose
public void onClose(Session session
, CloseReason closeReason) {
System.out.println("@Server CloseReason: "
+ closeReason);
}
@OnError
public void onError(Session session, Throwable thr) {
System.out.println("@Server Error: " + thr);
}
}
Komponentenbasierte Software-Entwicklung
Page 146
Prof. Dr. Stephan Kleuker
714
Erstes Beispiel (4/7): Client (1/3)
@ClientEndpoint
public class EchoClient {
@OnOpen
public void onOpen(Session session, EndpointConfig config) {
System.out.println("Id: " + session.getId()
+ "\nnegotiated: " + session.getNegotiatedSubprotocol()
+ "\nProtocol Version: " + session.getProtocolVersion()
+ "\nQuery String: " + session.getQueryString()
+ "\nRequestURI: " + session.getRequestURI()
+ "\nMaxIdleTimeout:" + session.getMaxIdleTimeout());
}
Komponentenbasierte Software-Entwicklung
Page 147
Prof. Dr. Stephan Kleuker
715
Erstes Beispiel (5/7): Client (2/3)
@OnMessage
public void onMessage(String message, Session session)
throws IOException {
System.out.println("@Client empfangen: " + message);
}
@OnClose
public void onClose(Session session, CloseReason closeReason) {
System.out.println("@Client CloseCode: "
+ closeReason.getCloseCode() + "\n@Client ReasonPhrase:"
+ closeReason.getReasonPhrase());
}
@OnError
public void onError(Session session, Throwable thr) {
System.out.println("@Client Error: " + thr);
}Komponentenbasierte Software-
Entwicklung
Page 148
Prof. Dr. Stephan Kleuker
716
Erstes Beispiel (6/7): Client (3/3)
public static void main(String[] args) {
WebSocketContainer container = ContainerProvider
.getWebSocketContainer();
try (Session session = container
.connectToServer(EchoClient.class, URI.create(
"ws://localhost:8080/WebSocketHelloWorld/echo"))) {
session.getBasicRemote().sendText("Hello");
session.getBasicRemote().sendText("Hello again");
System.out.println("1: " + session.isOpen());
session.close(new CloseReason( CloseCodes.NORMAL_CLOSURE
, "Schicht"));
System.out.println("2: " + session.isOpen());
} catch (Exception e) {
e.printStackTrace();
}
}
Komponentenbasierte Software-Entwicklung
Page 149
Prof. Dr. Stephan Kleuker
717
Erstes Beispiel (7/7): Ausgabe
Id: 2d80aea7-3d60-4a59-aaeb-56844cbbc25e
negotiated:
Protocol Version: 13
Query String: null
RequestURI: ws://localhost:8080/vlWebSocketEcho/echo
MaxIdleTimeout:0
1: true
@Client CloseCode: NORMAL_CLOSURE
@Client ReasonPhrase:Schicht
2: false
@Client empfangen: Hallo Client
INFO: @Server Anfrage URI: /vlWebSocketEcho/echo
INFO: @Server Nachricht: Hello
INFO: @Server Nachricht: Hello again
INFO: @Server CloseReason: CloseReason[1000,Schicht]
Komponentenbasierte Software-Entwicklung
Page 150
Prof. Dr. Stephan Kleuker
718
Analyse des ersten Beispiels
• Client und Server unterscheiden sich im Wesentlichen nur durch Annotationen @ClientEndpoint und @ServerEndpoint
• in JEE-Container führt @ServerEndpoint automatisch zum Deployen (läuft)
• beide nutzen @OnOpen, @OnMessage, @OnClose und gegebenenfalls @OnError
• zeigt Symmetrie der Kommunikationspartner
• wichtige (zu verwaltende) Objekte vom Typ Session
• Beispiel zeigt, dass es vom Timing abhängt, ob Bestätigung der zweiten Nachricht noch ankommt!
• keine explizite Nutzung von Threads notwendig; da paralleler Zugriff aber Synchronisation eventuell wichtig
Komponentenbasierte Software-Entwicklung
Page 151
Prof. Dr. Stephan Kleuker
719
2. Fallstudie (1/7): Realisierung eines Chats
• Clients können sich beim Server zum Chatten anmelden
• jede geschickte Nachricht wird an alle anderen verteilt
• Abmelden mit Nachricht „bye“ möglich
• (da wieder nur textbasiert, Überlappungen in Ein- und Ausgabe möglich)
• Server verwaltet Client-Sessions in einer synchronisierten Collection
Komponentenbasierte Software-Entwicklung
Page 152
Prof. Dr. Stephan Kleuker
720
2. Fallstudie (2/7): Server 1/2
@ServerEndpoint("/chat")
public class ChatServer {
private static Set<Session> partner =
Collections.synchronizedSet(new HashSet<Session>());
@OnOpen
public void onOpen(Session session, EndpointConfig config) {
this.partner.add(session);
}
@OnMessage
public void onMessage(String msg, Session s) throws IOException {
sende(msg);
}
@OnClose
public void onClose(Session session, CloseReason closeReason) {
this.partner.remove(session);
}
Komponentenbasierte Software-Entwicklung
Page 153
Prof. Dr. Stephan Kleuker
721
2. Fallstudie (3/7): Server 2/2
private void sende(String nachricht) {
try {
// nebenbei aufraeumen
List<Session> geschlossen = new ArrayList<>();
for (Session s : this.partner) {
if (!s.isOpen()) {
System.err.println("Geschlossen: " + s.getId());
geschlossen.add(s);
} else {
s.getBasicRemote().sendText(nachricht);
}
}
this.partner.removeAll(geschlossen);
System.out.println("Sende " + nachricht + " an "
+ this.partner.size() + " Klienten");
} catch (Throwable e) {
e.printStackTrace();
}
}
Komponentenbasierte Software-Entwicklung
Page 154
Prof. Dr. Stephan Kleuker
722
2. Fallstudie (4/7): Client 1/2
@ClientEndpoint
public class ChatClient {
@OnOpen
public void onOpen(Session session, EndpointConfig cfg) {
System.out.println("verbunden");
}
@OnMessage
public void onMessage(String msg, Session s) throws IOException {
System.out.println("@Client empfangen: " + msg);
}
@OnClose
public void onClose(Session session, CloseReason closeReason) {
System.out.println("abgemeldet");
}
Komponentenbasierte Software-Entwicklung
Page 155
Prof. Dr. Stephan Kleuker
723
2. Fallstudie (5/7): Client 2/2
public static void main(String[] args) {
WebSocketContainer container = ContainerProvider
.getWebSocketContainer();
try (Session session = container.connectToServer(ChatClient.class
, URI.create("ws://localhost:8080/WebSocketChat/chat"))) {
String eingabe= "";
while (!eingabe.toLowerCase().equals("bye")){
System.out.print("Beitrag: ");
eingabe = new Scanner(System.in).nextLine();
session.getBasicRemote().sendText(eingabe);
}
session.close(new CloseReason(CloseReason.CloseCodes
.NORMAL_CLOSURE, "Schicht"));
} catch (Exception e) {
e.printStackTrace();
}
}
} Komponentenbasierte Software-Entwicklung
Page 156
Prof. Dr. Stephan Kleuker
724
2. Fallstudie (6/7): Ausgabe (Eingaben markiert)
Beitrag: verbunden
Wer ist da
Beitrag: @Client
empfangen: Wer ist da
@Client empfangen:
ich
@Client empfangen:
ich auch
@Client empfangen:
bye
@Client empfangen:
bye
bye
@Client empfangen:
bye
abgemeldet
Beitrag: verbunden
@Client empfangen:
Wer ist da
ich
Beitrag: @Client
empfangen: ich
@Client empfangen:
ich auch
@Client empfangen:
bye
bye
@Client empfangen:
bye
abgemeldet
Beitrag: verbunden
@Client empfangen:
Wer ist da
@Client empfangen:
ich
ich auch
Beitrag: @Client
empfangen: ich auch
bye
@Client empfangen:
bye
abgemeldet
Komponentenbasierte Software-Entwicklung
Page 157
Prof. Dr. Stephan Kleuker
725
2. Fallstudie (7/7): Ausgabe Server
INFO: Sende Wer ist da an 3 Klienten
INFO: Sende ich an 3 Klienten
INFO: Sende ich auch an 3 Klienten
INFO: Sende bye an 3 Klienten
INFO: Sende bye an 2 Klienten
INFO: Sende bye an 1 Klienten
Komponentenbasierte Software-Entwicklung
Page 158
Prof. Dr. Stephan Kleuker
726
Ein- und Auspacken
• Zum Verschicken von Objekten werden sie in einfache Strings verwandelt
• hier bietet sich wieder JSON an
• für benötigte Klassen werden Encoder und Decoder geschrieben, die dem Client und Server bekannt gemacht werden
• folgendes Beispiel: Austausch von Sprint-Informationen mit JavaScript-Client (sehr elementar gehalten)
• Auch ByteStreams übertragbar
Komponentenbasierte Software-Entwicklung
Page 159
Prof. Dr. Stephan Kleuker
727
Sprint- Fallstudie (1/14): Sprints encoden 1/2
public class SprintsEncoder
implements Encoder.TextStream<List<Sprint>> {
@Override
public void encode(List<Sprint> sprints, Writer writer) {
JsonProvider provider = JsonProvider.provider();
JsonArrayBuilder elemente = Json.createArrayBuilder();
for (Sprint s : sprints) {
elemente.add(jsonSprint(s, false)); // von REST bekannt
}
JsonObject js = Json.createObjectBuilder()
.add("sprints", elemente).build();
try (JsonWriter jsonWriter =
provider.createWriter(writer)) {
jsonWriter.write(js);
}
}
was soll codiert werden
Komponentenbasierte Software-Entwicklung
Page 160
Prof. Dr. Stephan Kleuker
728
Sprint- Fallstudie (2/14): Sprints encoden 2/2
@Override
public void init(EndpointConfig config) {
}
@Override
public void destroy() {
}
//leider Copy & Paste
private JsonObject jsonSprint(Sprint s, boolean einzeln) {
...
}
Komponentenbasierte Software-Entwicklung
Page 161
Prof. Dr. Stephan Kleuker
729
Sprint- Fallstudie (3/14): Sprint decoden 1/2
public class SprintDecoder
implements Decoder.TextStream<Sprint> {
private SimpleDateFormat formatter
= new SimpleDateFormat("dd.MM.yyyy");
@Override
public Sprint decode(Reader reader){
JsonProvider provider = JsonProvider.provider();
JsonReader jsonReader = provider.createReader(reader);
JsonObject js = jsonReader.readObject();
Sprint sprint = new Sprint();
sprint.setMotto(js.getString("motto"));
try {
sprint.setStarttermin(formatter
.parse(js.getString("starttermin")));
was soll decodiert werden
Komponentenbasierte Software-Entwicklung
Page 162
Prof. Dr. Stephan Kleuker
730
Sprint- Fallstudie (4/14): Sprint decoden 2/2
sprint.setEndtermin(formatter
.parse(js.getString("endtermin")));
} catch (ParseException ex) {}
try {
sprint.setGeplanterAufwand(js.getInt("geplanterAufwand"));
} catch (Exception e){
sprint.setGeplanterAufwand(Integer
.parseInt(js.getString("geplanterAufwand")));
}
return sprint;
}
@Override public void init(EndpointConfig config) {}
@Override public void destroy() {}
}Komponentenbasierte Software-
Entwicklung
Page 163
Prof. Dr. Stephan Kleuker
731
Sprint- Fallstudie (5/14): SprintServer 1/3
@ServerEndpoint(value="/socketsprint"
, encoders={SprintsEncoder.class}
, decoders={SprintDecoder.class})
public class SprintServer implements Serializable{
@Inject
PersistenzService pers;
private static Set<Session> partner = Collections
.synchronizedSet(new HashSet<Session>());
@OnOpen
public void onOpen(Session session, EndpointConfig cfg) {
this.partner.add(session);
sende(); // besser nur an einen
}
Komponentenbasierte Software-Entwicklung
Page 164
Prof. Dr. Stephan Kleuker
732
Sprint- Fallstudie (6/14): SprintServer 2/3
@OnMessage
public void onMessage(Sprint sprint, Session session)
throws IOException {
try{
this.pers.persist(sprint);
sende();
} catch (Exception e){
// Benachrichtigung an den Client fehlt
}
}
@OnClose
public void onClose(Session session, CloseReason cR) {
this.partner.remove(session);
}
hier wird Decodierung genutzt
Komponentenbasierte Software-Entwicklung
Page 165
Prof. Dr. Stephan Kleuker
733
Sprint- Fallstudie (7/14): SprintServer 3/3
public void sende() {
try {
List<Session> geschlossen = new ArrayList<>();
List<Sprint> sprints = pers.findAllSprint();
for (Session s : this.partner) {
if (!s.isOpen()) {
System.err.println("Geschlossen: " + s.getId());
geschlossen.add(s);
} else {
s.getAsyncRemote().sendObject(sprints);
}
}
this.partner.removeAll(geschlossen);
} catch (Throwable e) {
e.printStackTrace();
}
}
}
Komponentenbasierte Software-Entwicklung
Page 166
Prof. Dr. Stephan Kleuker
734
Sprint- Fallstudie (8/14): JavaScript-Client 1/6
<!DOCTYPE html>
<html>
<head>
<title>WebSocket Client für Sprints</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">
<div id="eingabe">
Motto : <input type="text" id="motto"><br>
Starttermin: <input type="text" id="starttermin"><br>
Endtermin: <input type="text" id="endtermin"><br>
geplanter Aufwand: <input type="text"
id="geplanterAufwand"><br>
</div>
Komponentenbasierte Software-Entwicklung
Page 167
Prof. Dr. Stephan Kleuker
735
Sprint- Fallstudie (9/14): JavaScript-Client 2/6
<div id="button">
<input type="button" value="absenden"
onClick="sende();"><br>
<input type="button" value="beenden" onClick="ws.close();">
</div>
<div id="sprints"
style="background-color: white; margin:5px;">
</div>
</form>
</body>
</html>
Komponentenbasierte Software-Entwicklung
Page 168
Prof. Dr. Stephan Kleuker
736
Sprint- Fallstudie (10/14): JavaScript-Client 3/6
// Zuerst ueberpruefen, ob der Browser Websocket unterstuetzt
// wird am Ende des Scripts gestartet
if ("WebSocket" in window) {
var ws = new
WebSocket("ws://localhost:8080/Sprinter/socketsprint");
//wird bei erfolgreichem Verbindungsaufbau aufgerufen
ws.onopen = function() {
//alert("open");
};
// wird aufgerufen, wenn Server Daten schickt
ws.onmessage = function(event) {
// geht ohne eval
var objJSON = eval("(function()
{return " + event.data + ";})()");
document.getElementById("sprints")
.innerHTML = table(objJSON.sprints);
};Komponentenbasierte Software-
Entwicklung
Page 169
Prof. Dr. Stephan Kleuker
737
Sprint- Fallstudie (11/14): JavaScript-Client 4/6
//wird aufgerufen, wenn Verbindung geschlossen wurde
ws.onclose = function() {
alert("Client beendet Verbindung");
};
// Fehlermeldung
ws.onerror = function(error) {
alert("Ein Fehler ist aufgetretten " + error);
};
} else {
alert("Dein Browser ist zu alt");
}
Komponentenbasierte Software-Entwicklung
Page 170
Prof. Dr. Stephan Kleuker
738
Sprint- Fallstudie (12/14): JavaScript-Client 5/6
function sende() {
var sprint = {
motto: document.getElementById("motto").value,
starttermin: document.getElementById('starttermin').value,
endtermin: document.getElementById('endtermin').value,
geplanterAufwand:
document.getElementById('geplanterAufwand').value
};
//mit stringify zum JSON Objekt kodieren und abschicken
ws.send('' + JSON.stringify(sprint));
//Eingabefelder leeren
document.getElementById('motto').value = '';
document.getElementById('starttermin').value = '';
document.getElementById('endtermin').value = '';
document.getElementById('geplanterAufwand').value = '';
} Komponentenbasierte Software-Entwicklung
Page 171
Prof. Dr. Stephan Kleuker
739
Sprint- Fallstudie (13/14): JavaScript-Client 6/6
function table(data) {
var erg = "<table border='1'>";
erg += "<tr><th>Id</th><th>Motto</th><th>Start</th>";
erg += "<th>Ende</th><th>Geplanter Aufwand</th></tr>";
for (var i = 0; i < data.length; i++) {
erg += "<tr style='background-color:" + data[i].farbe + "'>";
erg += "<td>" + data[i].id + "</td>";
erg += "<td>" + data[i].motto + "</td>";
erg += "<td>" + data[i].starttermin + "</td>";
erg += "<td>" + data[i].endtermin + "</td>";
erg += "<td>" + data[i].geplanterAufwand + "</td>";
erg += "</tr>";
}
erg += "</table>";
return erg;
}Komponentenbasierte Software-
Entwicklung
Page 172
Prof. Dr. Stephan Kleuker
740
Sprint- Fallstudie (14/14): Beispielnutzung
Komponentenbasierte Software-Entwicklung
Page 173
Prof. Dr. Stephan Kleuker
741
nächste Schritte
• Anmerkung: Die Seite ist so mit Code-Injection angreifbar
• HTML-Client bekommt nicht mit, wenn in JSF-Applikation Sprint bearbeitet wird
• verschiedene Schritte denkbar
– Methode sende() von SprintServer wird aufgerufen, wenn ein Sprint-Objekt bearbeitet wird (z. B. in Persistieren einbauen)
– Persistierung erzeugt Events, wenn Sprint-Objekte bearbeitet werden, SprintServer abonniert diese
• nächste Folie; nur kleine Änderungen im SprintControllervon JSF (Änderungen durch REST-Client werden so nicht erkannt)
Komponentenbasierte Software-Entwicklung
Page 174
Prof. Dr. Stephan Kleuker
742
Verknüpfung JSF mit WebSocket-Server
@Named
@SessionScoped
public class SprintController implements Serializable {
@Inject
SprintServer server;
...
public String uebernehmen() { // analog loeschen
...
server.sende();
...
Komponentenbasierte Software-Entwicklung
Page 175
Prof. Dr. Stephan Kleuker
743
weitere Überlegung
Löschen und Bearbeiten prinzipiell kein Problem:
• es wird aber ein erweitertes Protokoll benötigt
• z. B. erste Eigenschaft gibt an, was gemacht werden soll
• dann würde nicht Sprint-Klasse sondern Befehlsklasse zum Dekodieren im Server genutzt
Weiterführend
• Übertragung von Byte-Streams, z. B, zum Verschicken von Bildern
Komponentenbasierte Software-Entwicklung
Page 176
Prof. Dr. Stephan Kleuker
744
Bedeutung von JavaScript
• Ursprünglich war JavaScript nur Hilfssprache, um kleine Berechnungen und Modifikationen im Browser zu ermöglichen
• mit HTML 5 wurde JavaScript zur zentralen Sprache des Internets
• Software-Engineering mit JavaScript steckt noch in den Kinderschuhen
• keine Klassenbibliothek, keine Standard-Frameworks
• eine unübersichtliche Menge sehr kreativer Lösungen
• Beispiel Varianten vom MV*-Pattern
• viele gute Werkzeuge und Hilfsmittel: JQuery, Jasmine, Istanbul, Karma, Selenium, …
Komponentenbasierte Software-Entwicklung