Inhaltsverzeichnis

Nicht immer besitzt eine Webanwendung auch wirklich eine grafische Weboberfläche. Manchmal reicht es auch schon aus, ein paar Funktionen über das Web entfernt aufrufbar zu machen. Wie das geht, erfährst du hier.

Lernziele dieser Einheit

Nach Abschluss dieser Einheit kannst du …

🏁

Kurzvorstellung von SOAP

Bildnachweis: Pixabay: rawpixel

HTTP + XML = SOAP

🌎 SOAP-Webservices erlauben Clients den entfernten Aufruf von Methoden eines Servers.
🌍 HTTP dient dabei als plattformunabhängiges Transportprotokoll für XML-SOAP-Nachrichten.
🌏 Üblicherweise wird es in Unternehmen zur Entwicklung betriebswirtschaftlicher Anwendungen genutzt.

Typische Anwendungsfälle

Sehr häufig werden Webservices dazu genutzt, B2B-Szenarios zwischen mehreren Unternehmen abzubilden, wofür ein Unternehmen Webservices bietet, die von seinen Kunden und Lieferanten aufgerufen werden können. Dadurch können die Systeme aller beteiligten Unternehmen untereinander Daten austauschen, um manuelle Prozessschritte zu eliminieren. Die nachfolgende Abbildung zeigt zum Beispiel, wie ein Großkunde eine Bestellung bei einem Händler aufgibt, was wiederum zur automatischen Erteilung eines Produktions- und eines Lieferauftrags bei zwei weiteren Unternehmen führt.

Systemintegration zwischen Unternehmen

Bildnachweis: Pixabay: Kaz

Innerhalb eines Unternehmens können Webservices genutzt werden, um heterogene Anwendungen über einen Integrationsserver miteinander zu verbinden. Üblicherweise bieten die Anwendungen, die meistens von unterschiedlichen Herstellern stammen und nicht miteinander kompatibel sind, hierfür eine Webservice-Schnittstelle, die durch den Integrationsserver bedient werden kann. Zusätzlich können auf dem Server weitere, durch die Anwendungen aufgerufene Webservices definiert werden, wobei der Server die Aufrufe umwandelt und dann entsprechend weiterleitet. Manche Server besitzen darüber hinaus sogar eine Regel-Engine, mit der automatisch ablaufende Geschäftsprozesse gesteuert werden können.

Integration heterogener Anwendungen

Technische Hintergründe zu SOAP

Jeder SOAP-Webservice besitzt eine WSDL-Beschreibung, in der alle aufrufbaren Operationen, die hierbei ausgetauschten XML-Nachrichten, das Transportprotokoll und die URL in einem maschinenlesbaren XML-Format definiert sind. Sie besitzt ungefähr folgenden Aufbau:

📃 Typdefinitionen der ausgetauschten XML-Daten mit XML Schema
📃 Definition der aufrufbaren Operationen (in Java würde man „Methoden” sagen)
📃 Zusammenfassung mehrere Operationen zu einem Interface (WSDL 2.0) bzw. Port Type (WSDL 1.1)
📃 Definition des Bindings mit dem Transportprotokoll, URL und Aufrufsemantik des Webservices

Das nachfolgende Beispiel zeigt die WSDL-1.1-Beschreibung für einen BuchladenWebservice mit sucheNachStichwort als einziger Operation:

<definitions name="BuchladenWebservice" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/"> <!-- Zunächst werden mit XML Schema alle XML-Strukturen definiert, die --> <!-- in irgend einer Form zwischen Client und Server ausgetauscht werden. --> <!-- Dabei muss es für jede Operation mindestens eine Anfrage- und eine --> <!-- Antwortstruktur geben. Darüber hinaus können aber noch beliebige --> <!-- weitere Strukturen definiert werden. --> <types> <xs:schema> <!-- Anfrage für „sucheNachStichwort” --> <element name="sucheNachStichwort"> … </element> <!-- Antwort für „sucheNachStichwort” --> <element name="sucheNachStichwortResponse"> … </element> <!-- Struktur für ein einzelnes Buch --> <element name="buch"> … </element> </xs:schema> </types> <!-- Anschließend werden für jede Operation die Anfrage- und die Antwort- --> <!-- Nachrichten des soap:Body definiert. Die Nachrichten beziehen sich --> <!-- dabei auf die Schemadefinitionen von oben. --> <message name="SucheNachStichwortRequestBody"> <part name="RequestBody" element="sucheNachStichwort"/> </message> <message name="SucheNachStichwortResponseBody"> <part name="ResponseBody" element="sucheNachStichwortResponse"/> </message> <!-- Darauf aufbauend können die Port Types mit den darin enthaltenen, --> <!-- aufrufbaren Operationen definiert werden. Jeder Port Type kann --> <!-- beliebig viele Operationen beinhalten und es kann auch beliebig --> <!-- viele Port Types geben. Für jede Operation wird auf die oben --> <!-- definierten XML-Nachrichten verweisen. --> <portType name="BuchSuchePortType"> <operation name="sucheNachStichwort"> <input message="tns:SucheNachStichwortRequestBody"/> <output message="tns:SucheNachStichwortResponseBody"/> </operation> </portType> <!-- Als nächstes folgt das Binding, das hauptsächlich HTTP als Protokoll --> <!-- zur Übertragung definiert und mit use="literal" festlegt, dass die --> <!-- XML-Nachrichten strikt dem XML-Schema entsprechen müssen. --> <binding name="BuchSucheBinding" type="tns:BuchSuchePortType"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="sucheNachStichwort"> <soap:operation> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> </binding> <!-- Und schlussendlich die URL zum Aufruf des Webservices. --> <service name="BuchSucheService"> <port name="BuchSuchePort" binding="tns:BuchSucheBinding"> <soap:address location="http://example.com/soap/buchsuche/"/> </port> </service> </definitions>

Anfrage und Antwort werden im SOAP-XML-Format kodiert und durch einen einfachen HTTP-Aufruf übertragen. Die aufgerufene URL, sowie der genaue Inhalte der XML-Nachrichten ergibt sich aus der WSDL-Beschreibung. Der Aufruf erfolgt immer als POST-Anfrage.

Webservices testen mit SoapUI

Bildnachweis für das Endesymbol: Pixabay: janf93

Aufgabe 1: Wir erkunden SoapUI

Lade dir von Moodle den Beispiel-Webservice herunter und lasse ihn in Netbeans laufen. Anschließend importiere seine beiden WSDLs in SoapUI und erkunde ein wenig die angeboten Operationen, indem du erst die Operation createExampleData des AdminSoapWebservice und dann die anderen Operationen ausführst. Vergleiche dabei auch, wie die Attribute der Movie und der Participant-Klassen im XML abgebildet werden.

Screenshot von SoapUI

Aufgabe 2: Ein kleines Webservice-Quiz

Aufgabe 2.1: Allgemeine Fragen

a) Für welche der typischen Anwendungsfälle wird SOAP typischerweise genutzt

  1. Zur Bereitstellung eines Serverbackends für clientseitige Webanwendungen
  2. Zur Automatisierung unternehmensübergreifender Geschäftsprozesse
  3. Zur Interprozesskommunikation mehrerer Programme auf demselben Rechner
  4. Zur Kommunikation von IoT-Devices mit einem Server in der Cloud
  5. Zur Integration unterschiedlicher Anwendungen verschiedener Hersteller

b) Webservices besitzen häufig eine HTML-Oberfläche.

  1. Wahr
  2. Falsch

c) Welche der folgenden Aussagen stimmt nicht?

  1. Webservices setzen voraus, dass Client und Server in Java geschrieben sind.
  2. Webservices nutzen HTTP und andere Webtechniken zum maschinellen Datenaustausch.
  3. SOAP-Webservices nutzen darüber hinaus XML als Nachrichtenformat.

Aufgabe 2.2: Fragen zur Technik

a) Welche Aufgabe erfüllt die WSDL eines SOAP-Webservices?

  1. Sie dient als menschenlesbare Dokumentation des Webservices.
  2. Sie dient als maschinenlesbare, formale Definition des Webservices.
  3. Sie dient als Beispielcode zum Aufruf des Webservices.

b) Welche der folgenden WSDL-Versionen werden von Java unterstützt?

  1. WSDL 1.1
  2. WSDL 2.0

c) Welche Aufgabe erfüllen die Port Types bzw. Interfaces einer WSDL?

  1. Sie definieren die Struktur der ausgetauschten XML-Nachrichten.
  2. Sie definieren die Namen aller vorhandenen Operationen.
  3. Sie fassen mehrere Operationen zusammen und weisen ihnen XML-Nachrichten zu.
  4. Sie definieren das Transportprotokoll und die URL des Webservices.

d) Welche HTTP-Verben kann ein Client zum Aufruf eines SOAP-Webservices verwenden?

  1. GET, PUT, POST oder DELETE, je nach aufgerufener Operation
  2. Nur GET und POST
  3. Nur POST und DELETE
  4. Nur GET
  5. Nur POST
  6. Nur PUT

e) Welches Beispiel entspricht am ehestem dem Aufbau einer SOAP-Nachricht?

  1. <soap:Envelope xmlns:soap="…"> <soap:HeaderFields> … </soap:HeaderFields> <soap:BodyContent> … </soap:BodyContent> </soap:Envelope>
  2. <soap:Soap xmlns:soap="…"> <soap:head> … </soap:head> <soap:body> … </soap:body> </soap:Soap>
  3. <soap:Envelope xmlns:soap="…"> <soap:Header> … </soap:Header> <soap:Body> … </soap:Body> </soap:Envelope>

Lösung: Aufgabe 2.1: 2 + 5, 2, 1
Aufgabe 2.2: 2, beide, 3, 5, 3

SOAP-Webservices mit Java

Bildnachweis: Pixabay: rawpixel

Eigene Webservices definieren

☀️ Für die Entwicklung von SOAP-Webservices werden in Java die JAX-WS-Klassen verwendet.
☀️ Serverseitig muss hierfür nur eine Stateless Session Bean mit weiteren Annotationen ausgestattet werden.
☀️ Clientseitig lässt sich der benötigte Quellcode anhand der WSDL automatisch generieren.

Definition des Webservices auf Serverseite

Code First: Eine bestehende Javaklasse wird durch Annotationen zu einem Webservice ausgebaut. Java erzeugt automatisch eine dazu passende WSDL. Hierfür kann das Kommandozeilenwerkzeug wsgen bzw. die in Netbeans enthaltene GUI dafür verwendet werden. Durch Anhängen von ?wsdl an die Webserivce-URL lässt sich diese jedoch auch zur Laufzeit abrufen.

Aufruf von wsgen für Code-First-Ansatz

Contract First: Das serversitige Codegerüst wird anhand einer vorhandenen WSDL generiert. Die WSDL wird hierfür mit dem Werkzeugprogramm wsimport bzw. der in Netbeans enthaltenen GUI eingelesen und verarbeitet.

Aufruf von wsimport für Contract-First-Ansatz

Verwendung des Webservices auf Clientseite

Sämtliche benötigten Klassen lassen sich mit wsimport bzw. der GUI in Netbeans generieren. Auf der übernächsten Folie schauen wir uns das genauer an. Hier geht es zunächst noch nur darum, die Serverseite zu programmieren.

Aufruf von wsimport zur Generierung der Clientklassen

@WebService

Kennzeichnet eine öffentliche, nicht-abstrakte Klasse mit öffentlichem und parameterlosen Konstruktor als Webserviceimplementierung.

@WebMethod

Markiert eine öffentliche, nicht-statische Methode, um sie über den Webservices aufrufbar zu machen.

@WebParam

Kennzeichnet einen Methodenparameter und legt fest, wie er im XML übergeben werden muss, z.B. wie das dazugehörige XML-Tag heißen muss.

@WebResult

Steht vor einer Methode und legt fest, welchen Namen des XML-Tag heißt, in dem der Rückgabewert an den Aufrufer gesendet wird.

Hinweis JAX-WS kümmert sich automatisch um die Konvertierung der Methodenparameter und Rückgabewerte von und nach XML. Dabei können auch komplexe Objekte nach XML und zurück umgewandelt werden.
Bildnachweis: Pixabay: Alexandra_Koch

Innerhalb der Java Enterprise Edition dürfen nur Stateless Session Beans als Webservice gekennzeichnet werden. Es empfiehlt sich dabei, immer eine eigene EJB nur für den Webservice zu definieren, anstatt einfach die ohnehin schon vorhandenen EJBs zum Webservice zu machen. Dadurch hat man eine klarere Trennung und läuft nicht Gefahr, zu viele Methoden entfernt aufrufbar zu machen. So wie im nachfolgenden Beispiel, das einen Webservice zur Verwaltung von Zirkusvorstellungen definiert. 🎪

/** * Zugrunde liegende EJB zur Verwaltung von Spielorten. * Hier nutzen wir die EntityBean aus dem JPA-Kapitel. */ @Stateless public class LocationBean extends EntityBean<Location, Long> { public LocationBean() { super(Location.class); } } /** * Zugrunde liegende EJB zur Verwaltung von Vorstellungen. * Nutzt ebenfalls die EntityBean aus dem JPA-Kapitel. */ @Stateless public class GigBean extends EntityBean<Gig, Long> { public GigBean() { super(Gig.class); } } /** * Und hier nun der eigentliche Webservice. Entfernte Clients können nur * seine Methoden aufrufen und somit zum Beispiel keine Änderungen an den * Daten vornehmen. */ @Stateless @WebService public class CircusWebservice { @EJB LocationBean locationBean; @EJB GigBean gigBean; public CircusWebservice() { } @WebMethod @WebResult(name="location") public List<Location> getAllLocations() { return this.locationBean.findAll(); } @WebMethod @WebResult(name="gig") public List<Gig> getAllGigs() { return this.gigBean.findAll(); } @WebMethod @WebResult(name="gig") public Gig getGigDetails(@WebParam(name="id") long id) { return this.gigBean.findById(id); } }

Ein Wort zur Sicherheit

Ein Webservice sollte immer mit einer Benutzer- und Berechtigungsprüfung ausgestattet sein, um unberechtigte Zugriffe zu verhindern.
Die Konfiguration hängt allerdings vom jeweiligen Applikationsserver ab und bestimmte Dinge sind im Standard gar nicht vorgesehen.
Für SOAP ist es daher am einfachsten sich nicht auf den Server zu verlassen, sondern alles selbst zu programmieren.

Der folgende Webservice besitzt zwei Operationen: Eine, mit der ein neuer Benutzer angelegt werden kann und eine weitere, die bereits einen gültigen Benutzer verlangt. Beide Operationen greifen auf eine EJB namens UserBean zu, um einen Benutzer anzulegen bzw. seine Zugangsdaten zu prüfen. Die Clou ist dabei die Methode userBean.validateUser(), die eine Exception wirft, wenn die Zugangsdaten nicht stimmen oder wenn dem Benutzer nicht die erforderliche Benutzergruppe zugewiesen wurde.

@Stateless @WebService public class SOAPAuthBeispielWebservice { @EJB UserBean userBean; /** * Neuen Benutzer registrieren. Geht natürlich, ohne sich zuvor zu * authentifizieren. :-) Die Felder "username" und "password" werden * hierfür im SOAP-Body übertragen. */ @WebMethod @WebResult(name = "status") public String signup( @WebParam(name = "username") String username, @WebParam(name = "password") String password) throws UserBean.UserAlreadyExistsException { this.userBean.signup(username, password, "soap-user"); return "OK"; } /** * Beispiel für eine geschützte Methode. Erfordert, dass der Client im * SOAP-Header die Felder "username" und "password" sendet. */ @WebMethod @WebResult(name = "message") public String getSecretMessage( @WebParam(name = "username", header = true) String username, @WebParam(name = "password", header = true) String password) throws UserBean.InvalidCredentialsException, UserBean.AccessRestrictedException { // Wirft eine Exception, wenn der Benutzer nicht berechtigt ist this.userBean.validateUser(username, password, "soap-user"); // Der geschützte Code, den nicht jeder ausführen darf return "Streng geheim! For your eyes only!"; } }

Die UserBean beinhaltet die beiden Methoden zum Anlegen eines Benutzers und zum Überprüfen seiner Berechtigungen. Intern greift die Klasse hierfür auf eine Persistence Entity zu. Am Ende der Klasse befinden sich die Exceptions, die bei ungültigen Zugangsdaten geworfen werden.

@Stateless public class UserBean { @PersistenceContext EntityManager em; /** * Registrieren eines neuen Benutzers */ public void signup(String username, String password, String... groups) throws UserAlreadyExistsException { // Sicherstellen, dass es den Benutzer noch nicht gibt if (em.find(User.class, username) != null) { throw new UserAlreadyExistsException("Der Benutzername existiert bereits."); } // Neuen Benutzer erzeugen User user = new User(username, password); for (String group : groups) { user.addToGroup(group); } em.persist(user); } /** * Anmeldedaten eines Benutzers sowie Benutzergruppenzuordnung und Zugehörigkeit * zu einer der übergebenen Benutzergruppen prüfen. */ public User validateUser(String username, String password, String... groups) throws InvalidCredentialsException, AccessRestrictedException { // Benutzer suchen und Passwort prüfen User user = em.find(User.class, username); boolean authorize = false; if (user == null || !user.checkPassword(password)) { throw new InvalidCredentialsException("Benutzername oder Passwort falsch."); } // Zugeordnete Benutzergruppen prüfen, mindestens eine muss vorhanden sein for (String group : groups) { if (user.groups.contains(group)) { authorize = true; break; } } if (!authorize) { throw new AccessRestrictedException("Sie sind hierfür nicht berechtigt."); } // Alles okay! return user; } //<editor-fold defaultstate="collapsed" desc="Exceptions"> /** * Fehler: Der Benutzername ist bereits vergeben. */ public class UserAlreadyExistsException extends Exception { public UserAlreadyExistsException(String message) { super(message); } } /** * Fehler: Anmeldedaten des Benutzers stimmen nicht */ public class InvalidCredentialsException extends Exception { … } /** * Fehler: Benutzer besitzt nicht die erforderliche Benutzergruppe. */ public class AccessRestrictedException extends Exception { … } //</editor-fold> }

Der Benutzer selbst ist eine einfache Persistence Entity, wobei hier ein paar Besonderheiten berücksichtigt werden, wie zum Beispiel, dass das Passwort nur als Hashwert gespeichert wird. Dementsprechend gibt es weiter unten ein paar Hilfsmethoden zum Änderung und Prüfen des Passworts.

@Entity @Table(name = "SOAP_USER") public class User implements Serializable { @Id @Column(name = "USERNAME", length = 64) private String username; @Column(name = "PASSWORD_HASH", length = 64) private String passwordHash; @ElementCollection @CollectionTable( name = "SOAP_USER_GROUP", joinColumns = @JoinColumn(name = "USERNAME") ) @Column(name = "GROUPNAME") List<String> groups = new ArrayList<>(); //<editor-fold defaultstate="collapsed" desc="Konstruktoren"> public User() { } public User(String username, String password) { this.username = username; this.passwordHash = this.hashPassword(password); } //</editor-fold> //<editor-fold defaultstate="collapsed" desc="Setter und Getter"> public String getUsername() { return username; } public void setUsername(String id) { this.username = id; } //</editor-fold> //<editor-fold defaultstate="collapsed" desc="Passwort setzen und prüfen"> /** * Berechnet der Hash-Wert zu einem Passwort. */ private String hashPassword(String password) { byte[] hash; if (password == null) { password = ""; } try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); hash = digest.digest(password.getBytes(StandardCharsets.UTF_8)); } catch (NoSuchAlgorithmException ex) { hash = "!".getBytes(StandardCharsets.UTF_8); } BigInteger bigInt = new BigInteger(1, hash); return bigInt.toString(16); } /** * Berechnet einen Hashwert aus dem übergebenen Passwort und legt ihn im * Feld passwordHash ab. Somit wird das Passwort niemals als Klartext * gespeichert. * * Gleichzeitig wird das Passwort im nicht gespeicherten Feld password * abgelegt, um durch die Bean Validation Annotationen überprüft werden * zu können. */ public void setPassword(String password) { this.passwordHash = this.hashPassword(password); } /** * Prüft, ob das übergebene Passwort korrekt ist. */ public boolean checkPassword(String password) { return this.passwordHash.equals(this.hashPassword(password)); } //</editor-fold> //<editor-fold defaultstate="collapsed" desc="Zuordnung zu Benutzergruppen"> /** * @return Eine unveränderliche Liste aller Benutzergruppen */ public List<String> getGroups() { List<String> groupsCopy = new ArrayList<>(); this.groups.forEach((groupname) -> { groupsCopy.add(groupname); }); return groupsCopy; } /** * Fügt den Benutzer einer weiteren Benutzergruppe hinzu. */ public void addToGroup(String groupname) { if (!this.groups.contains(groupname)) { this.groups.add(groupname); } } /** * Entfernt den Benutzer aus der übergebenen Benutzergruppe. */ public void removeFromGroup(String groupname) { this.groups.remove(groupname); } //</editor-fold> }

Fremde Webservices aufrufen

Bildnachweis für das Endesymbol: Pixabay: janf93

Aufgabe 3: Eine kleine Seifenoper

Was liegt näher, als mit SOAP eine Daily Soap zu programmieren? 💘 Du weißt schon, diese täglichen Serien im Vorabendprogramm, die sich immer um den Alltag derselben Personen drehen. 💑 Hierfür haben wir auf Moodle eine Vorlage mit einem dazu passenden Datenmodell vorbereitet. Lade diese herunter und importiere sie in Netbeans.

a) In der Vorlage ist bereits eine Klasse namens AdminWebservice mit der Methode createExampleData enthalten. Vervollständige die Methode so, dass weitere Datensätze für Personen und Beziehungen in der Datenbank abgelegt werden.

b) Schreibe eine neue Klasse namens SeifenoperWebservice, die einen SOAP-Webservice mit folgenden Operationen anbietet:

  • List<Person> findAllPersonen()
  • List<Beziehung> findBeziehungByPerson(Person person)
  • Beziehung saveNewBeziehung(Beziehung beziehung)
  • Beziehung updateBeziehung(Beziehung beziehung)

Nutze die vorgegebenen Methoden der PersonBean und BeziehungBean zum Ausprogrammieren der Methoden. Achte dabei darauf, dass die Methodenparameter und Rückgabewerte sprechende Namen im XML erhalten.

Screenshot des Webservices in SoapUI

Admin-Webservice

@Stateless @WebService(serviceName = "seifenoper") public class AdminWebservice { @EJB BeziehungBean beziehungBean; @EJB PersonBean personBean; @WebMethod @WebResult(name = "status") public String createExampleData() throws ParseException { // Alte Einträge komplett löschen for (Beziehung beziehung : this.beziehungBean.findAll()) { this.beziehungBean.delete(beziehung); } for (Person person : this.personBean.findAll()) { this.personBean.delete(person); } // Neue Personen anlegen Person joeCool = new Person("Joe Cool", 1983, Geschlecht.MAENNLICH, "Der Schwarm aller Frauen"); this.personBean.saveNew(joeCool); Person gerdFleißig = new Person("Gerd Fleißig", 1971, Geschlecht.MAENNLICH, "Ist mit seiner Arbeit verheiratet"); this.personBean.saveNew(gerdFleißig); Person juliaHofer = new Person("Julia Hofer", 1982, Geschlecht.WEIBLICH, "War mal mit Gerd zusammen"); this.personBean.saveNew(juliaHofer); … // Neue Beziehungen anlegen SimpleDateFormat sdf = new SimpleDateFormat("yyyy-M-dd"); Beziehung beziehung; beziehung = new Beziehung(joeCool, juliaHofer, sdf.parse("2016-12-03"), false, "Haben sich auf dem Weihnachtsmarkt kennengelernt"); this.beziehungBean.saveNew(beziehung); beziehung = new Beziehung(gerdFleißig, juliaHofer, sdf.parse("1996-04-21"), sdf.parse("2001-10-10"), false, "Haben zusammen studiert"); this.beziehungBean.saveNew(beziehung); … // Status zurückgeben return "OK"; } }

Seifenoper-Webservice

@Stateless @WebService(serviceName = "seifenoper") public class SeifenoperWebservice { @EJB BeziehungBean beziehungBean; @EJB PersonBean personBean; @WebMethod @WebResult(name = "person") public List<Person> findAllPersonen() { return this.personBean.findAll(); } @WebMethod @WebResult(name = "beziehung") public List<Beziehung> findBeziehungByPerson(@WebParam(name = "personId") long personId) { Person person = this.personBean.findById(personId); return this.beziehungBean.findByPerson(person); } @WebMethod @WebResult(name = "beziehung") public Beziehung saveNewBeziehung(@WebParam(name = "beziehung") Beziehung beziehung) { return this.beziehungBean.saveNew(beziehung); } @WebMethod @WebResult(name = "beziehung") public Beziehung updateBeziehung(@WebParam(name = "beziehung") Beziehung beziehung) { return this.beziehungBean.update(beziehung); } }

Aufgabe 4: Ein kleines JAX-WS-Quiz

a) Was bedeutet das Vorgehen „Contract First” für die Entwicklung von SOAP-Webservices?

  1. Ausgehend von einer als Webservice deklarierten Javaklasse wird eine WSDL-Datei generiert.
  2. Ausgehend von einer auf anderem Wege erstellten WSDL wird das Codegerüst in Java generiert-

b) Welche Annotation steht vor eine Klasse und zeichnet sie als Webservices aus?

  1. @SOAPBinding
  2. @WebResult
  3. @WebService
  4. @WebMethod

c) Welche Annotation steht vor einer Methode und zeichnet sie als Webservice-Operation aus?

  1. @WebserviceContext
  2. @WebOperation
  3. @PortType
  4. @WebMethod

d) Welche Annotation steht vor einem Methodenparameter, um seinen Namen zu setzen?

  1. @WebParam
  2. @WebResult
  3. @Remote
  4. @Parameter

e) Welche Möglichkeiten bietet Java EE zur Erstellung eines Webservices?

  1. Ein Servlet kann durch Annotationen zu einem SOAP-Webservice umgewandelt werden.
  2. Eine beliebige Enterprise Java Bean kann durch Annotationen zu einem Webservice erweitert werden.
  3. Eine Stateful Session Bean kann durch Vererbung zu einer Webservice-Implementierung gemacht werden.
  4. Eine Stateless Session Bean kann Teile ihrer Methoden als Webservice zur Verfügung stellen.

f) Welches Werkzeug dient der Generierung von Javacode anhand einer WSDL?

  1. wsgen
  2. wsimport
  3. javac
  4. soapui

Lösung: 2, 3, 4, 1, 4, 2

Gegeben sind folgende Webservice-Klassen. Schreibe das XML für eine beispielhafte SOAP-Anfrage sowie eine Antwort mit mindestens zwei Suchergebnissen.

@Stateless @WebService public class FilmDatenbank { @WebMethod @WebResult(name="film") List<Film> sucheFilm(@WebParam(name="query") String q) { … } } @Entity public class Film { @Id @GeneratedValue private long id = 0L; private String name = ""; private int jahr = 0; // Konstruktoren … // Setter und Getter … }
<!-- SOAP-Anfrage --> <soap:Envelope xmlns:soap="…"> <soap:Header /> <soap:Body> <sucheFilm> <query>Bibi und Tina</query> </sucheFilm> </soap:Body> </soap:Envelope> <!-- SOAP-Antwort --> <soap:Envelope xmlns:soap="…"> <soap:Header /> <soap:Body> <sucheFilmResponse> <film> <name>Bibi auf Weltreise</name> <jahr>1991</jahr> </film> <film> <name>Tinas Abenteuer</name> <jahr>1993</jahr> </film> </sucheFilmResponse> </soap:Body> </soap:Envelope>

Hinweise zum Schluss

Bildnachweis: Pixabay: rawpixel

Do & Don't

Definition eines Webservices

Sicherheit

Betrieb eines Webservices

Aufruf fremder Webservices

Rechtshinweise

Creative Commons Namensnennung 4.0 International

§