# Abwärtskompatibilität eines WebServices



## raphaa (24. Sep 2009)

Hallo Java Freunde,

welche Möglichkeiten gibt es, einen WebService abwärtskompatibel zu gestalten?

Zum Szenario:
Ich benötige einen WebService, der Daten, in Form eines Objektes, von mehreren Clients entgegen nehmen kann und persistent macht (z.B. in eine Textdatei oder eine Datenbank). Das Client-Objekt, welches zum WebService-Server geschickt wird, kann in diversen Versionen vorliegen. Eine neue Version des Client-Objekts entsteht, wenn der Client ein Update der Anwendung ausführt. Das Resultat eines Updates, kann z.B. sein, dass das zu übertragene Objekt ein weiteres Attribut enthält. Der vom Client angesprochene WebService, befindet sich stets in der aktuellsten Programmversion (d.h. er hat auch immer die aktuellste Objekt-Version).
Nehmen wir an, NEU ist die neueste, ALT eine ältere und ÄLTER eine noch ältere Objekt-Version. Wenn alle Beteiligten NEU sind, dann sollte es kein Problem darstellen. Wenn eine beteiligte Client-Anwendung z.B. noch kein Update gefahren hat, befindet sich das zu übertragene Client-Objekt in der Version ALT. Was würde dann passieren? Nehmen wir noch an, dass der WebService ein weiteres Update enthält und sich der Client immer noch nicht upgedatet hat. Somit befindet sich der Client im Zustand ÄLTER (und soll aber trotzdem vom WebService verarbeitet werden können).
Soviel zum Szenario.

Welche Möglichkeiten bestehen ein solches Szenario zu realisieren? Ich würde gerne so etwas wie einen Versions-Verteiler-WebServie vermeiden, da sonst für jede Version des Clients ein WebService zur Verfügung stehen muss. Die Logik auf dem Server sollte auch nicht Versiongetrieben sein. Dadurch würde ich nur eine Schnittstelle haben, jedoch stets wachsenden, redundanten Verarbeitungs-Code. 

Habt ihr Ideen, ein solches Szenario sinnvoll zu gestalten? Welche Möglichkeiten gibt es? Ich sehe meine beiden Möglichkeiten nicht als Ansätze einer Lösung, lasse mich aber gerne des Besseren belehren.

Ich lasse diese Anfrage einfach mal so im Raum stehen, um es wirken zu lassen, und bin sehr dankbar für Antworten.

Grüße


----------



## Vayu (25. Sep 2009)

naja halte es wie bei einer public api, "don't change the method signature"
wenn du an einer methode etwas verändern möchtest führe eine neue methode ein. so bleiben die alten erhalten und abwärtskompatibel


----------



## Stefoan (25. Sep 2009)

Meinst Du, dass sich die Daten (also Dateien, etc...), oder die Klassen für die Übertragung, updaten? Also willst Du eine Versionierung der Daten oder eine Art Online-Update für die Anwendung?


----------



## raphaa (26. Sep 2009)

Stefoan hat gesagt.:


> Meinst Du, dass sich die Daten (also Dateien, etc...), oder die Klassen für die Übertragung, updaten? Also willst Du eine Versionierung der Daten oder eine Art Online-Update für die Anwendung?



Hi. Das Online-Update für die Anwendung passiert schon. Das Update beinhaltet die Aktualisierung der Anwendung und eine Aktualisierung der Klassen für die Übertragung.

Bsp: Stell dir eine Klasse 'StiftSortiment' vor. Basis, d.h. die Attribute der Klasse, sind zu Anfang 'bleistift' und 'kugelschreiber'. Der WebService (incl. Schnittstellenbeschreibung) ist, in gleicher Version wie die Klasse, darauf ausgerichtet, diese Attribute der Klasse zu übertragen. Nach dem Update der Anwendung, d.h. der WebService und die Klasse, sind beide dafür ausgerichtet ein weiteres Attribut zu übertragen, z.B. 'filzstift'. Jetzt kann es sein, dass ein Client, welcher sich noch nicht upgedatet hat, das Attribut 'filzstift' noch nicht besitzt. Wie würde die Übertragung dann aussehen? Um die Schwierigkeitsstufe zu erhöhen, kann durch ein Update auch ein Attribut entzogen werden. Das sind alles Problemstellungen für die ich Ideen suche, um einen abwärtskompatiblen WebService zu erstellen.

Ich hoffe diese weitere Info hilft etwas weiter...

Danke im Voraus für weitere Vorschläge!


----------



## raphaa (26. Sep 2009)

Vayu hat gesagt.:


> naja halte es wie bei einer public api, "don't change the method signature"
> wenn du an einer methode etwas verändern möchtest führe eine neue methode ein. so bleiben die alten erhalten und abwärtskompatibel



Danke für die Idee.
Wenn ich jedoch überlege, dass mehrere Tausend Clients auf den WebService zugreifen sollen, müsste der WebService im schlimmsten Fall genau so viele Methoden haben, wie es Clients gibt. Und das ist meines erachtens nicht sinn der Sache, oder?


----------



## Vayu (26. Sep 2009)

ist es denn so, dass du mit jedem update deines services auch jede Methode veränderst? Sorry dann ist dein Konzept nicht durchdacht 

Änderungen an einer Public API sollten immer abwärtskompatibel sein, sprich vorher ein durchdachtes design aufstellen. Wenn hinterher dann doch etwas geändert werden muss (bug oder ähnliches), was dann auswirkungen entweder auf die Methodensignatur oder aber noch schlimmer auf die funktion der methode hat, 

sprich in version 1.0 gibt funktion add(2,3) 6 zurück und in version 1.1 5 (ist jetzt vllt n blödes beispiel, aber mir fällt grad kein besseres ein )

dann sollte statt diese Methode zu korrigieren lieber eine neue Methode eingeführt werden und die alte deprecated geflagged werden.


----------



## raphaa (26. Sep 2009)

Vayu hat gesagt.:


> ist es denn so, dass du mit jedem update deines services auch jede Methode veränderst? Sorry dann ist dein Konzept nicht durchdacht



Sorry, ich habe mich wahrscheinlich falsch ausgedrückt. Die Signatur der WebServices-Methode bleibt natürlich immer gleich. Nur das zu übertragene Objekt ändert sich.

Wenn ich z.B. eine WebService-Methode des Clients wie folgt habe...

public void sendObjekt(StiftSortiment parameterStiftSortiment) { ... }

ändert sich bei einem Update nur das Objekt StiftSortiment (siehe Beispiel oben). Das zu übertragene Objekt bekommt dann durch ein Update ein Attribut hinzu oder ein Attribut wird entnommen. 

Was hat das für Auswirkungen?


----------



## Vayu (26. Sep 2009)

naja vom prinzip genau dieselben auswirkungen 

dein service sollte also damit zurechtkommen, dass bei einem Benutzer einer alten Version ein Attribute fehlen kann.


----------



## Stefoan (29. Sep 2009)

Ich denke nicht, dass es eine gute Idee ist, die Klasse zu ändern, wenn sich der Bestand der Waren ändert! Übergib doch lieber( z.B. beim Start der Anwendung,) eine Liste mit den relevanten Waren, wie z.B. :

```
class Warenliste { 
   List<Ware> aktuelleWaren = new ArrayList<Ware>(); 
}
```
Diese befüllst Du dann mit den aktuellen Waren, z.B. :

```
class Kugelschreiber extends Ware {}
class Filzstift extends Ware {}
...
```
und verschickst sie danach. Bei Bedarf _erweiterst _Du deine Anwendung dann um neue Waren, _änderst aber nicht_ ständig die Beschaffenheit der Klassen.

Hoffe ich habe Dich richtig verstanden, bis später.

Gruß Stefan


----------



## Keo (30. Sep 2009)

Was spricht denn dagegen, wenn das Update-Verfahren versiongetrieben ist? Die Clients senden ihre letzte Update-Version an den Server und erhalten als Rückgabe alle ihre Updates. Der Returnwert müsste dann so dynamisch gestaltet werden, dass er mit den Updates zurecht kommt.


----------



## raphaa (30. Sep 2009)

Hi Stefoan,
leider ist dies keine Möglichkeit. Die vom Client zu übertragenden Daten sind in einem persistenten Objekt gekappselt und sollen über einen Web-Service auf einen Server gesendet werden und dort auch persistent gemacht werden. Ich denke wir sollten uns vom Beispiel des StiftSortiments lösen. Ein anderes Beispiel wäre der Auftrag für einen Paketdienst. Der Auftrag beinhaltet also alle Daten zur Absender- und Empfängeradresse und die zu vermittelten Pakete. Nehmen wir diesbezüglich auch an, dass in einer ALT-Version des Clients, dementsprechend auch des 'Auftrag'-Objektes, die Absenderadresse fehlt und in der NEU-Version vorhanden ist. Das könnten z.B. 3 Attribute sein, wie Absendername, -straße und -ort Wenn nun ein Update für die Clients zur Verfügung stehen, bedeutet das auch, dass sich das Objekt 'Auftrag', welches als Parameter an die Web-Service-Methode gesendet wird, ändert. Die Signatur der Methode sollte jedoch gleich bleiben (wenn dies überhaupt zu der Problemstellung möglich ist). Der Konflikt entsteht, wie schon im Verlauf des Beitrags deutlich gemacht, in der Objektversion. Die Clients, die noch kein Update der Anwendung gefahren haben, besitzen das Objekt 'Auftrag' ohne Absenderinfos. Der Web-Service jedoch, der immer auf dem 
aktuellsten Objekt-Stand ist, besitzt das neue Auftragsobjekt incl. Absenderdaten.

Ich versuche es im folgenden zu visualisieren...

Methode des Web-Services:
public void sendeAuftragsdaten(Auftrag parameterAuftrag) {...}
(dabei hat der Parameter-Auftragsobjekt die NEU-Struktur incl. Absenderdaten)

Methodenaufruf des Clients:
Auftrag localAuftrag;
WebService.sendeAuftragsdaten(localAuftrag);
(der nicht upgedatete Client besitzt das alte Auftragsobjekt ohne Absenderdaten)

Ich habe versucht den Versionskonflikt und die Problematik der Abwärtskompatibilität noch etwas verständlicher zu machen. Feedback wär kool...

Danke im Voraus


----------



## raphaa (30. Sep 2009)

Keo hat gesagt.:


> Was spricht denn dagegen, wenn das Update-Verfahren versiongetrieben ist? Die Clients senden ihre letzte Update-Version an den Server und erhalten als Rückgabe alle ihre Updates. Der Returnwert müsste dann so dynamisch gestaltet werden, dass er mit den Updates zurecht kommt.



Hi, Keo.
Der Update-Mechanismus spielt in diesem Zusammenhang keine Rolle. Es bleibt einfach Fakt, dass mehrere Clients nicht die aktuelle Version des Objektes besitzen. Die Problematik befindet sich immernoch bei der Abwärtskompatibilität.


----------



## Keo (30. Sep 2009)

> Methode des Web-Services:
> public void sendeAuftragsdaten(Auftrag parameterAuftrag) {...}
> (dabei hat der Parameter-Auftragsobjekt die NEU-Struktur incl. Absenderdaten)
> 
> ...



verstehe ich Dich richtig, dass abhänig von der Client-Version immer eine andere Auftragsstruktur gesendet werden muss, weil die Datenelemente in dieser Struktur fest definiert sind? Kannst Du diese Struktur nicht dynmaisch halten, in der Art:


```
public class Auftrag
    Object[][] auftragContents;
    .....
```

auftragcontens[0][0] enthält dann den Feldnamen und dazugehörigen Wert, z.B. Feldname=Strasse, Wert=Ludwingstrasse


----------



## raphaa (1. Okt 2009)

Keo hat gesagt.:


> ```
> public class Auftrag
> Object[][] auftragContents;
> .....
> ```



Hey Keo,
die Klasse würde folgendermaßen aussehen...

```
public class Auftrag
    String absenderName;
    String absenderStraße;
    String absenderOrt;
    String empfaengerName;
    String empfaengerStraße;
    String empfaengerOrt;
    List<Paket> auftragsPakete;
```

Meines erachtens wäre deine Idee der dynamischen Datenübertragung über den WebService eine gute Lösung. Die Logik des Clients würde dann aus dem Objekt Auftrag so eine Struktur (Object[][] auftragContents aus den Attributen und dazugehörigem Wert bauen um sie dann über den WebService zu übertragen. Auf der Serverseite würde auch eine Logik diese Daten wieder zurück zu dem entsprechenden Objekt verwandeln.

Für so eine Lösung stellt sich mir jetzt die Frage (weil ich eben bei Google auf die schnelle nichts gefunden habe), ob es Möglich ist, aus einem Objekt die Variablennamen und evtl. auch deren Werte als String zurückzugeben, welche ich dann in einer Collection oder Array speichern könnte. Mir schwebt so etwas wie
"Object[][] auftragContents = auftragsObjekt.getClass().getContents()" und auch anders herum vor. Des Weiteren wär vielleicht eine Möglcihkeit sinnig, um die Attribute in einem Objekt zu prüfen z.B. auftragsObjekt.getClass().existVar("empfaengerStrasse"). 

Würden diese Möglichkeiten bestehen, denke ich hätte ich die Lösung gefunden, weil ich dann auf Variablen prüfen könnte und wenn z.B. das AuftragsObjekt über einen Update eine neue Variable hinzubekommt, könnte ich den mit den String-Variablennamen der Collection (ob das Attribut vorhanden ist). Wenn die zu übertragende Collection den Variablennamen nicht besitzt könnte ich ja durch die Logik diese neue Variable des Server-Objekts auf null setzten. Bei den übereinstimmenden Variablen würde ich einfach den Wert im neuen AuftragsObjekt setzen, welches ich mit der Collection bzw. Array aufbaue.

Ein Feedback zu dieser Idee wär sehr hilfreich. Danke im Voraus!


----------



## Keo (6. Okt 2009)

> Für so eine Lösung stellt sich mir jetzt die Frage (weil ich eben bei Google auf die schnelle nichts gefunden habe), ob es Möglich ist, aus einem Objekt die Variablennamen und evtl. auch deren Werte als String zurückzugeben, welche ich dann in einer Collection oder Array speichern könnte




```
public class Auftrag
    String absenderName;
    String absenderStraße;
    String absenderOrt;
    String empfaengerName;
    String empfaengerStraße;
    String empfaengerOrt;
    AuftragExtension[] extension;
....
//getter & setter
```


```
public class AuftragExtension 
    String varName;
    String varValue;
    Long varNum;
    Date varTime;
....
//getter & setter
```


----------

