diff --git a/.github/workflows/validate-blogpost.yml b/.github/workflows/validate-blogpost.yml
index 7596e0a1a..d818edfc6 100644
--- a/.github/workflows/validate-blogpost.yml
+++ b/.github/workflows/validate-blogpost.yml
@@ -11,11 +11,11 @@ jobs:
runs-on: ubuntu-latest
permissions:
issues: write
-
+
steps:
- name: Inject env
uses: rlespinasse/github-slug-action@v3.x
-
+
- uses: actions/checkout@v2
- name: git log
@@ -32,7 +32,7 @@ jobs:
env:
ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true'
working-directory: /home/runner/work/devblog/devblog
-
+
- uses: actions/checkout@v2
with:
repository: ${{ github.event.pull_request.user.login }}/${{ env.GITHUB_REPOSITORY_NAME_PART }}
diff --git a/_config.yml b/_config.yml
index 388d120fb..f35963ffe 100644
--- a/_config.yml
+++ b/_config.yml
@@ -44,6 +44,9 @@ kramdown:
encoding: "utf-8"
markdown_ext: "markdown,mkdown,mkdn,mkd,md"
+feed:
+ path: feed.xml
+
# Konfiguration für das Erstellen der Bilder
mini_magick:
fullScreen_1600:
diff --git "a/_posts/2021-08-23-functional-kotlin-eine-einf\303\274hrung.md" "b/_posts/2021-08-23-functional-kotlin-eine-einf\303\274hrung.md"
new file mode 100644
index 000000000..c8a45ef27
--- /dev/null
+++ "b/_posts/2021-08-23-functional-kotlin-eine-einf\303\274hrung.md"
@@ -0,0 +1,371 @@
+---
+layout: [post, post-xml] # Pflichtfeld. Nicht ändern!
+title: "Functional Kotlin - Eine Einführung" # Pflichtfeld. Bitte einen Titel für den Blog Post angeben.
+date: 2021-08-23 13:00 # Pflichtfeld. Format "YYYY-MM-DD HH:MM". Muss für Veröffentlichung in der Vergangenheit liegen. (Für Preview egal)
+modified_date: 2021-08-23 13:00 # Optional. Muss angegeben werden, wenn eine bestehende Datei geändert wird.
+author_ids: [fabianvolkert, jo2] # Pflichtfeld. Es muss in der "authors.yml" einen Eintrag mit diesem Namen geben.
+categories: [Softwareentwicklung] # Pflichtfeld. Maximal eine der angegebenen Kategorien verwenden.
+tags: [Kotlin, Funktionale Programmierung] # Bitte auf Großschreibung achten.
+---
+In diesem Blogeintrag widmen wir uns der Programmiersprache Kotlin.
+Wir werfen einen kurzen Blick auf die Ursprünge der Sprache, wie sie aufgebaut ist und mit welchen Designprinzipien im Hinterkopf sie entworfen wurde.
+An Beispielen betrachten wir die Best Practices und stellen uns dabei die Frage: Was hat das mit funktionaler Programmierung zu tun?
+
+# Kotlin
+
+Vor zehn Jahren (2011) stellte JetBrains erstmals die Open-Source Programmiersprache Kotlin auf dem [JVM Language Summit](https://blog.jetbrains.com/kotlin/2011/07/hello-world-2/) vor -- "Eine Sprache einfach genug für den gewöhnlichen Entwickler und produktiv genug für moderne Anforderungen an Projekte".
+Die Sprache setzt auf der Java Virtual Machine (JVM) auf und erschien 2016 in der ersten Release-Version.
+Seit 2017 wird sie von Google offiziell zur Entwicklung von Android-Apps unterstützt und ist seit 2019 Googles bevorzugte Sprache für diese Plattform.
+
+Im [TIOBE-Index](https://www.tiobe.com/tiobe-index/)[^1] rangiert Kotlin aktuell (Juli 2021) auf Platz 38 der beliebtesten Programmiersprachen.
+Betrachtet man ausschließlich die JVM-spezifischen Sprachen steht Kotlin dort auf Platz 4 (übertrumpft von #36 Scala, #15 Groovy und #2 Java).
+
+Im [PYPL-Index](https://pypl.github.io/PYPL.html)[^2] belegt Kotlin den 11. Platz.
+
+[^1]:TIOBE zählt die gefundenen Ergebnisse zu Programmiersprachen aus verschiedenen Suchmaschinen.
+[^2]:PYPL nutzt Google Trends für Tutorial-Suchanfragen.
+
+Kotlin, eine statisch typisierte Programmiersprache, ist voll interoperabel zu Java-Programmen und -Bibliotheken und kann ohne aufwändige Integration in bereits bestehende Projekte eingepflegt werden.
+
+## Grundlagen
+Jeder Einstieg in eine Programmiersprache fängt gleich an.
+Man muss sich an die Syntax gewöhnen.
+Folgendes Beispiel zeigt ein kleines, in Kotlin geschriebenes Programm, welches zuerst das Ergebnis einer Instanzmethode und dann das einer statischen Methode ausgibt:
+```kotlin
+fun main(){
+ val mainClassInstance = MainClass("instanceString")
+ println(mainClassInstance.instanceMethod())
+ println(MainClass.staticMethod(3))
+}
+
+class MainClass(private var member: String){
+ companion object{
+ fun staticMethod(parameter:Int):Int{
+ return parameter*2
+ }
+ }
+
+ public fun instanceMethod():String{
+ return "A"
+ }
+}
+```
+
+Das [`companion object`](https://kotlinlang.org/docs/object-declarations.html#companion-objects) ist, wie der Name es andeutet, ein Begleiterobjekt zu dieser Klasse und verhält sich ähnlich der statischen Initialisierung in Java (auch wenn das Begleiterobjekt noch einiges mehr kann, auf das ich hier nicht eingehen werde).
+Anders als in Java werden die Rückgabewerte von Methoden am Ende des Methodenkopfs plaziert und auch für Wertdefinitionen und Parameter wird der jeweilige Typ durch einen ":" getrennt auf die rechte Seite gestellt.
+Kotlin unterstützt [Typinferenz](https://kotlinlang.org/spec/type-inference.html), weswegen die Typdefinitionen in den meisten Fällen auch weggelassen werden können.
+Was -- anders als bei Java -- hier auch auffällt, ist, dass ich die *Properties* der `MainClass` direkt hinter den Klassennamen in "( )" definieren kann und sie nicht im Codeblock schreiben muss (, aber auch das könnte ich).
+Kotlin generiert für die Variablen Getter- und Setter-Methoden und für die Values nur Getter-Methoden.
+Auf den Unterschied komme ich im Abschnitt [Immutabilität](#immutabilität) zu sprechen.
+Wie das Beispiel oben auch zeigt, habe ich einen primären Konstruktor für die `MainClass`geschrieben, der sich direkt im Header befindet.
+Die in den Klammern des Konstruktors angegebenen Properties (hier *member*) entsprechen direkt einer Deklaration dieser als Teil der Klasse.
+Auch die Semikolons können wir in den meisten Fällen weglassen.
+
+## Nullsicherheit
+Eine Ärgerlichkeit, mit der wir uns im Entwicklungsalltag häufig auseinandersetzen müssen, ist das Behandeln von Nullpointer-Exceptions, also dem Fehlen von Daten an Stellen, an denen das Programm welche erwartet hat.
+Tony Hoare, der Erfinder der Null-Referenz, hielt 2009 einen Vortrag und nannte als Grund für dessen Einführung die Einfachheit, mit der sie zu implementieren gewesen sei.
+Er bezeichnet seine Entscheidung inzwischen als "Milliarde-Dollar-Fehler":
+([My billion-dollar mistake](https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare/))
+Kotlin behandelt dieses Problem aus meiner Sicht pragmatisch ([Null-Safety](https://kotlinlang.org/docs/null-safety.html)), indem, wenn nicht anders angegeben, Werte einfach nicht null sein dürfen.
+Betrachten wir folgendes kleines Beispiel eines Produkts, für das ein Preis mit Steuer berechnet werden soll.
+```kotlin
+fun main() {
+ val product = Product(4.99)
+ val vat: Double = null
+ product.getConsumerPrice(vat)
+}
+
+class Product(private val price: Double) {
+ fun getConsumerPrice(vat: Double): Double {
+ val tempValue = helperFunction(vat)
+ return tempValue * this.price
+ }
+ ...
+}
+```
+Was tendenziell in Java und vielen anderen Sprachen funktioniert wird hier vom Compiler mit einer Fehlermeldung quittiert, da der Wert `val vat: Double` als Double mit Wert definiert und null dort nicht erlaubt ist.
+So werden wir bei der Entwicklung immer informiert, wenn Daten potentiell undefinierte Zustände annehmen könnten:
+`"Kotlin: Null can not be a value of a non-null type Double"`
+Manchmal lässt es sich allerdings auch nicht vermeiden oder ist erwünscht, dass ein null Wert übernommen wird;
+Das kann zum Beispiel an Schnittstellen der Fall sein, an denen ein Standardwert keinen Sinn ergibt (auch wenn sich hier wieder darüber streiten lässt, ob ein Standardwert wirklich nicht die bessere Entscheidung ist).
+Wir können die Werte mit einem `?` markieren, um kenntlich zu machen, dass sie null (*nullable*) sein dürfen:
+`val vat: Double? = null`
+Hier kommt die Arbeit zum Vorschein, die uns der Compiler durch diese kleine Änderung abnimmt: `product.getConsumerPrice(vat)` wird mit dem `Double?`aufgerufen, aber `getConsumerPrice(vat: Double)` erwartet einen Wert, der nicht null ist.
+Auch das erkennt der Compiler und gibt `Type mismatch: inferred type is Double? but Double was expected`zurück.
+So sind wir gezwungen, uns um diesen Fall zu kümmern und entweder vorher sicherzustellen, dass `vat` nicht null sein kann, oder einen Nullwert als Eingabeparameter zu erlauben, wodurch sich die Fehlermeldung auf nachfolgende Aufrufe von `vat` verbreitet.
+
+Ein anderes Beispiel zeigt, wie wir uns beim Programmieren mit Nullwerten in Kotlin viel Boilerplate-Code sparen können.
+Dafür schauen wir uns zunächst ein Problem in Java und anschließend eine Lösung in Kotlin an:
+```java
+class Head {
+ public Node next;
+}
+class Node {
+ public Node next;
+ String value = null;
+}
+...
+head.next.next.value;
+```
+Die Suche von `value` in dem Beispiel kann, wenn einer der Zwischenaufrufe null ist, zu einer `NullpointerException` führen.
+Um dieses Problem zu umgehen, müssen wir zwischen den Aufrufen null-Checks einführen.
+Um Platz zu sparen schreibe ich die Prüfungen direkt als ternäre Operationen:
+```java
+Head head = new Head();
+Node nodeA = head.next != null? head.next :null;
+Node nodeB = nodeA.next != null? nodeA.next :null;
+String value = nodeB != null? nodeB.value: null;
+```
+In Kotlin kann bei potentiellen Nullwerten `?` eingesetzt werden, um diese zu erlauben:
+```java
+class Head(val next: Node?)
+class Node(val next: Node?, val value: String)
+...
+val head = Head(null) // Bei der Initialisierung muss ich den Wert für 'next' direkt angeben und kann ihn nicht unbestimmt lassen
+val string = head.next?.next?.value
+```
+Dieser Aufruf führt zu keiner `NullpointerException`, sondern weist `string` null zu, da bereits der Aufruf von `head.next?` null zurückgibt.
+Der Wert ist dabei implizit vom Typ `String?`, wodurch auch alle folgenden Aufrufe vom Compiler wieder geprüft werden.
+
+Alternativ kann der Elvis-Opeator `?:` genutzt werden, um in solchen Fällen direkt einen Standardwert zuzuweisen, sodass statt `String?` der Typ `String` inferiert wird.
+```Kotlin
+val string = head.next?.next?.value?:"default"
+```
+
+So kann sichergestellt werden, dass Nullwerte innerhalb der Anwendung angemessen behandelt werden können.
+
+## Immutabilität
+In den beiden Beispielen der vorherigenen Sektionen habe ich das `val`- und das `var`-Schlüsselwort zur Definition von Werten genutzt.
+`val` wird genutzt um einen zur Laufzeit unveränderlichen Wert zu definieren (anders noch als `const`, welches für unveränderliche, zur Kompilierzeit bekannte, Werte steht).
+Es ist vergleichbar mit `final` aus Java.
+Auf der anderen Seite steht das `var`-Schlüsselwort mit dem herkömmliche Variablen beschrieben werden können.
+Immutabilität hilft während der Entwicklung Nebeneffekte im Code auf ein Minimum zu reduzieren und schafft so Sicherheit vor allem für Parallelität.
+Betrachten wir folgendes Beispiel in Java:
+```Java
+class SideEffect {
+ public int member = 0;
+
+ public int someCalculation(int input) {
+ int aux = member + 2;
+ int result = member + aux + input;
+ member++;
+ return result;
+ }
+}
+```
+`someCalculation` nutzt die Variable `member` für einige Berechnungen.
+In einer synchronen Umgebung ist dies problemlos möglich.
+Soll die Methode allerdings parallel ausgeführt werden, kann es zu inkonsistentem Verhalten kommen, da `member` zu verschiedenen Zeitpunkten innerhalb der Ausführung der Methode unterschiedliche Werte annehmen kann.
+Besser ist hier eine Lösung, die `someCalculation` weitestgehend unabhängig vom aktuellen Wert von `member`macht.
+Denkbar ist:
+```Java
+public int someCalculation(int input, int memberVal) {
+ int aux = memberVal + 2;
+ int result = memberVal + aux + input;
+ member++;
+ return result;
+}
+```
+Durch das Verlagern des für die Berechnung genutzten Wertes ist sichergestellt, dass die Methode, selbst wenn sich `member` zur Laufzeit ändert, innerhalb ihres Ausführungskontextes einen konsistenten Zustand einhält.
+An dieser Stelle bediene ich mich zusätzlich an einigen Punkten, auf die man zum Thema Immutabilität im Internet immer wieder trifft:
+- Threadsicherheit (Durch Zugriff auf Werte, die sich nicht ändern)
+- Keine versteckten Nebeneffekte (Es gibt kein Risiko, dass Methoden unbemerkt Werte an anderen Stellen ändern)
+- Sicherheit vor Nullwerten (Wenn ein Wert einmal überprüft wurde, behält er seine Gültigkeit)
+- Leichteres Caching (Wenn ein Wert einmal geladen wurde und sich Rahmenbedingungen ändern, ist sichergestellt, dass dieser Wert nach wie vor gültig ist und nicht neu geladen werden muss)
+- Bessere Kapselung von Methoden und Klassen (Es ist sichergestellt, dass Methoden und Klassen, die untereinander kommunizieren, sich nicht gegenseitig verändern)
+- Einfacher zu Testen (Durch feste Werte und fehlende Nebeneffekte sind die Punkte, die es bei Fehlern zu überprüfen gilt, weniger und einfacher)
+- Leichtere Lesbarkeit und Wartbarkeit (Geht einher mit der leichteren Testbarkeit)
+- Vorhersagbarkeit (Wenn Werte konkret sind, können zuverlässige Annahmen getroffen werden)
+
+Diese Auflistung zeigt, dass der Aufwand für Immutabilität im Verältnis zu den Vorteilen in den meisten Fällen gering ausfällt.
+
+# Funktionale Programmierung
+An dieser Stelle macht es Sinn, die funktionalen Programmierung ins Spiel zu bringen.
+Was ist funktionale Programmierung und wie kann sie uns bei unserer Arbeit helfen?
+Die funktionale Programmierung ist ein Ansatz der Programmierung, die Verarbeitung von Daten nicht anweisungsgetrieben (imperativ, wie z.B. in Java) zu konzipieren, sondern aus einer mathematischen Perspektive heraus -- funktional -- zu betrachten.
+Also statt dass wir ein Problem aus der Perspektive betrachten, jeden Schritt einzeln durchzugehen, arbeiten wir mit einer Menge von Daten, auf die Operationen angewandt werden und die mitunter eine neue Menge von Daten erzeugt.
+Die funktionale Programmierung ist etwas, was in vielen großen Programmiersprachen immer mehr Einzug hält, auch weil die Rechenleistung heutiger Computer so hoch ist, dass die schlechtere Performance, die durch diesen Ansatz erreicht wird, nicht mehr ins Gewicht fällt.
+Theoretische Grundlage der funktionalen Programmierung ist das Lambda-Kalkül, welches in den 30er Jahren von Church und Kleene zur Beschreibung von Funktionen eingeführt wurde.
+Ein einfacher Lambda-Ausdruck sieht dabei wie folgt aus und beschreibt hier f(x)=x+2:
+```
+λx.x+2
+```
+Lambda-Ausdrücke kennen wir aus der Entwicklung im Java-Kontext hauptsächlich in Form von Lambda-Ausdrücken (ab Java 8 in 2014).
+Collections müssen dazu erst in einen *stream* konvertiert, transformiert und dann dann wieder zurück konvertiert werden:
+```Java
+strings
+ .stream()
+ .filter(s -> s.length() == 5)
+ .collect(Collectors.toList());
+```
+Lambda-Ausdrücke gibt es auch in Kotlin und werden dort viel häufiger verwendet.
+Gibt es nur einen Wert im Lambda-Ausdruck der gebunden werden muss, kann der implizite Name *it* benutzt werden, statt dem Laufwert einen konkreten Namen geben zu müssen.
+```Kotlin
+strings.filter { it.length == 5 }
+```
+Auch wenn beide Beispiele hier nur einfache sind, empfinde ich persönlich die Kotlin-seitigen Lösungen häufig intuitiver und kürzer als das bei Java der Fall ist.
+Allein der Wegfall der Konvertierungen reduziert den Boilerplate-Code und erleichtert damit die Wartung der Software.
+
+## map, reduce, filter, ...
+Die `filter`-Methode haben wir gerade eben kennengelernt.
+Wie der Name beschreibt, kann sie genutzt werden, um Elemente aus einer Menge an Daten herauszufiltern.
+Die zwei wichtigen anderen Methoden, die häufig eingesetzt werden, sind die `map`- und die `reduce`-Methode.
+`map` iteriert über jedes Element einer Menge von Daten und wendet eine Funktion auf dieses an.
+Heraus kommt dabei eine neue Menge von Daten, die möglicherweise geändert wurden.
+(Ich sage möglicherweise, weil die identische Abbildung f(x)=x existiert)
+```Kotlin
+productList.map { product -> product.getConsumerPrice(0.19) }
+```
+Obiges Beispiel zeigt, wie eine Liste von Produkten in eine Liste von Preisen konvertiert wird, indem von jedem Produkt-Element der Konsumentenpreis geholt wird.
+Die `reduce`-Methode verhält sich ähnlich zur `map`-Methode, mit dem Unterschied, dass das Ergebnis ein einzelnes Element ist.
+Auch hier wird auf jedes Element der Menge eine Funktion angewandt.
+Das folgende Beispiel zeigt, wie aus unserer Preisliste eine Summe über alle Preise gebildet wird.
+`sum` definiert dabei das Akkumulator-Element im ersten Parameter.
+`price` ist die Laufvariable (eher Laufwert) für die einzelnen Preise, über die iteriert wird.
+```Kotlin
+priceList.reduce { sum, price -> sum+price }
+```
+Statt uns mit der Iteration beschäftigen zu müssen, erlaubt diese Heransgehensweise uns das eigentliche Problem behandeln zu können.
+`map`, `filter` und `reduce` sind Beispiele für sogenannte [Funktionen höherer Ordnung](https://kotlinlang.org/docs/lambdas.html), denn sie nehmen nicht nur einfache Werte als Parameter entgegen, sondern erwarten Funktionen, die sie während ihrer Ausführung aufrufen können.
+Ihre Flexibilität im Kern, während sie einen klaren Rahmen für die Verarbeitung von Daten in einer bestimmten Art und Weise schaffen, machen sie zu mächtigen Werkzeugen.
+
+## Extension Functions
+
+```Kotlin
+inline fun Iterable
Welche Möglichkeiten haben wir, wenn neben einer REST-API auch eine SOAP-Schnittstelle zur Verfügung stehen soll? +In diesem Artikel wird eine Methode vorgestellt, wie man diese Anforderung mit Hilfe von Kong umsetzen kann.
+ +]]>Welche Möglichkeiten haben wir, wenn neben einer REST-API auch eine SOAP-Schnittstelle zur Verfügung stehen soll? +In diesem Artikel wird eine Methode vorgestellt, wie man diese Anforderung mit Hilfe von Kong umsetzen kann.
+ +Wie bereist beschrieben, kann es manchmal gewollt sein, dass eine Schnittstelle über REST und SOAP erreichbar ist. +Hier stellt sich nun die Frage wie man diese Anforderung realisiert.
+ +Eine intuitive Herangehensweise wäre es, für den Service die zwei Schnittstellen getrennt zu implementieren. +Diese Lösung hat allerdings den Nachteil, dass die Schnittstellen sich durch die getrennte Implementierung unterschiedlich verhalten könnten. +Außerdem müssen bei einer Änderung des Service beide Schnittstellen verändert werden, womit zusätzlicher Arbeitsaufwand verbunden wäre.
+ +Um diese Probleme zu vermeiden, könnten wir für den Service nur eine Schnittstelle implementieren, zum Beispiel eine REST-API, da diese heutzutage häufig verwendet wird. +Die SOAP-Schnittstelle hingegen könnten wir anschließend aus der REST-API generieren.
+ +Für die Realisierung beider Schnittstellen wird ein Kong API Gateway mit dem Kong Plugin soap2rest sowie ein REST-Service benötigt.
+ +
Die Abbildung zeigt, wie die einzelnen Komponenten miteinander verknüpft sind. +Das Kong API Gateway verwaltet den Zugriff auf den REST-Service. +Für den Fall, dass wir eine Anfrage über die REST-Schnittstelle stellen, wird diese Anfrage an den Service weitergeleitet und bearbeitet.
+ +Für die Verarbeitung von SOAP-Anfragen wird das Kong Plugin soap2rest verwendet. +Das Plugin benötigt zur Konfiguration zwei Dateien. +Damit das Plugin die SOAP-Anfragen richtig verarbeiten kann, benötigt es die WSDL der SOAP-Schnittstelle. +Und um die Konvertierung von SOAP zu REST Anfragen korrekt durchzuführen, wird zusätzlich die OpenAPI-Spezifikation der REST-API benötigt.
+ +
Diese Abbildung zeigt den Fall, dass eine Anfrage über die SOAP-Schnittstelle gestellt wird. +Sobald Kong eine Anfrage über die Route der SOAP-Schnittstelle registriert, wird das Plugin soap2rest ausgeführt. +Das Plugin konvertiert die Anfrage in eine gültige REST-Anfrage und sendet diese an den REST-Service. +Nachdem der REST-Service geantwortet hat, wird die Antwort in eine gültige SOAP-Antwort übersetzt und zurückgegeben.
+ +Anhand des folgenden Beispiels lässt sich der Ablauf näher verdeutlichen. +Angenommen, folgende Anfrage wird an die SOAP-Schnittstelle gestellt.
+ +<?xml version="1.0" encoding="utf-8"?>
+<soap:Envelope
+ xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:tns="http://www.w3.org/2001/XMLSchema">
+ <soap:Body>
+ <tns:GetPetByPetid_InputMessage>
+ <tns:petId>1</tns:petId>
+ </tns:GetPetByPetid_InputMessage>
+ </soap:Body>
+</soap:Envelope>
+
+
+Aus dieser Anfrage lassen sich verschiedene Informationen ableiten.
+Zum einen wird mit dieser Anfrage die SOAP-Action GetPetByPetid ausgeführt.
+Zusätzlich beinhaltet die Anfrage den Parameter petId mit dem Wert 1.
+Anhand dieser Informationen kann die Anfrage der passenden REST Anfrage zugeordnet werden.
+In diesem Fall entspricht die Anfrage dem Pfad /pet/1.
Nachdem das Plugin die generierte Anfrage an die REST-API gestellt hat, bekommt es folgende Antwort:
+ +{
+ "id": 1,
+ "name": "doggie",
+ "photoUrls": [],
+ "tags": [],
+ "status": "available"
+}
+
+
+Anschließend wird diese Antwort vom Plugin in gültiges XML umgewandelt und in eine SOAP-Antwort eingesetzt, welche anschließend zurückgegeben wird.
+ +<?xml version="1.0" encoding="UTF-8"?>
+<soap:Envelope
+ xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:tns="http://www.w3.org/2001/XMLSchema">
+ <soap:Body>
+ <tns:GetPetByPetid_OutputMessage>
+ <tns:Pet>
+ <tns:id>1</tns:id>
+ <tns:name>doggie</tns:name>
+ <tns:photoUrls></tns:photoUrls>
+ <tns:tags></tns:tags>
+ <tns:status>available</tns:status>
+ </tns:Pet>
+ </tns:GetPetByPetid_OutputMessage>
+ </soap:Body>
+</soap:Envelope>
+
+
+Die Analyse der WSDL und der OpenAPI ist ein wichtiger Bestandteil des Plugins. +Sie wird nur einmal vor der ersten Anfrage an die SOAP-Schnittstelle ausgeführt, um die Schnittstelle zu konfigurieren. +Dadurch verzögert sich die Antwort der ersten Anfrage im Durchschnitt um 200 Millisekunden. +Anschließend wird diese Konfiguration für die Verarbeitung aller folgenden Anfragen verwendet.
+ +Bei der Analyse der WSDL werden wichtige Merkmale der SOAP-Schnittstelle ausgelesen. +Dazu gehören zum Beispiel das Auslesen der verschiedenen Operationen und ihrer zugehörigen Rückgabetypen und Faults. +Es wird besonders die Struktur der Rückgabetypen betrachtet, damit das Plugin später gültige SOAP-Antworten generieren kann.
+ +Nachdem die WSDL analysiert wurde, wird die Konfiguration des Plugins mit der OpenAPI-Spezifikation der REST-API vervollständigt. +Dabei werden den SOAP-Operationen die passenden Pfade der REST-API automatisiert zugeordnet. +Außerdem werden jeder Operation die passenden Content-Types zugeordnet, damit die HTTP-Header bei der Weiterleitung der Anfragen an die REST-API korrekt gesetzt werden können.
+ +Bei der Konvertierung von eingehenden SOAP-Anfragen, werden diese in den meisten Fällen direkt von XML in JSON überführt. +Manchmal müssen davor aber noch andere Verarbeitungsschritte durchgeführt werden. +Zum Beispiel werden sämtliche SOAP-Header in HTTP-Header überführt. +Außerdem werden Dateiuploads in Multipart Bodys überführt und den Dateien wird mit Hilfe einer Mime Type Analyse der richtige Content-Type zugeordnet.
+ +Auch bei der Konvertierung der REST-Antworten werden verschiedene Zwischenschritte benötigt. +Wenn die REST-API nicht den Statuscode 200 zurückgibt, wird die Antwort in ein gültiges SOAP-Fault umgewandelt.
+ +Bei der Umwandlung der Antwort in XML wird besonders darauf geachtet, dass die Reihenfolge der Attribute des Rückgabetyps mit der Reihenfolge in der WSDL übereinstimmen. +Dafür wird die automatisch generierte Konfiguration des Plugins verwendet.
+ +Damit der Einsatz des Plugins sich lohnt, sollte die Verfügbarkeit der Schnittstelle nicht unter dem Einsatz des Plugins leiden. +Um die Auswirkungen des Plugins auf die Verfügbarkeit der Schnittstelle zu testen, wurde ein Performance- und ein Lasttest vorgenommen.
+ +Der Performancetest besteht aus vier verschiedenen Anfragen, welche jeweils 10-mal wiederholt wurden. +Zwei der vier Anfragen liefern nur einen HTTP Status Code von 200 und 300 zurück. +Die dritte Anfrage sendet mit einem HTTP POST ein kleines JSON Objekt an die Schnittstelle. +Und die vierte Anfrage versucht die Grenzen der Schnittstelle auszuloten, indem eine Datei an die Schnittstelle gesendet wird.
+ +Die nachfolgende Grafik zeigt links die Ergebnisse des Performancetests auf die REST-API und rechts die Ergebnisse der SOAP-Schnittstelle.
+ +
Im direkten Vergleich fällt auf, dass die Performance nur beim Senden von Dateien leidet. +Die Performance aller andern Anfragen verändert sich nur geringfügig.
+ +Der Anstieg der Antwortzeit ist darauf zurückzuführen, dass bei SOAP-Anfragen deutlich mehr Daten an die Schnittstelle gesendet werden müssen.
+ +Im Gegensatz zum Performancetest wurden die vier Anfragen 250-mal parallel ausgeführt. +Um ein aussagekräftiges Ergebnis zu erhalten wurde dieser 10-mal wiederholt.
+ +Die folgende Grafik zeigt die Ergebnisse des Lasttests.
+ +
Auf den ersten Blick fällt auf, dass die SOAP-Schnittstelle bei vielen parallelen Anfragen längere Antwortzeiten aufweist, als die REST-API. +Vor allem das Senden von Dateien führt zu einem deutlichen Anstieg der Antwortzeit. +Der Kommunikationsaufwand in Verbindung mit den vielen gleichzeitigen Anfragen auf die Schnittstelle ist für die langsamere Antwortzeit verantwortlich.
+ +Zusammenfassend lässt sich sagen, dass die Nutzung dieses Kong Plugins den Vorteil hat, dass nicht beide Schnittstellen implementiert werden müssen. +Ein Nachteil ist die zusätzliche Wartezeit auf die Antwort der Schnittstelle, da sich hinter jeder SOAP-Anfrage eine Anfrage auf die REST-API verbirgt. +Diese Verzögerung kann allerdings vernachlässigt werden, da erst bei vielen parallelen Anfragen ein deutlich längere Wartezeit entsteht. +Im Großen und Ganzen überwiegen die Vorteile des Plugins die Nachteile.
+ +