MagicMap WebServices
aus Nomads, der freien Wissensdatenbank
In MagicMap erfolgt die Kommunikation zwischen Client(en) und Server(n) über eine Web Service-Schnittstelle, mit deren Hife Kartendaten und Positionen von Objekten übermittelt werden. Dazu wird das Java Design Pattern "Facade" verwendet. Das heißt, jede Facade fasst bestimmte Methoden zusammen, die der Client jeweils als Web Service aufrufen kann. Die Web Services einer Facade werden durch jeweils eine WSDL-Datei (Web Service Definition Language) spezifiziert. Insgesamt wird die MagicMap-Schnittstelle durch die 4 folgenden WSDL-Dateien beschrieben:
- sessionFacade: Ermöglicht über die Funktionen connect und disconnect das Erstellen und Beenden einer Verbindung zum Server.
- mapFacade: Hier sind alle wichtigen Funktionen enthalten, um neue Karten auf dem Server anzulegen oder um Daten zu bereits bestehenden Karten abzufragen.
- positionFacade: Bietet unter anderem die Möglichkeit einzelne Objekte, wie z.B. Acces Points oder Clients auf einer Karte anzulegen oder zu verändern.
- version: Diese WSDL dient zur Versionsabfrage der Server-Version.
Diese Seite beschreibt dazu die Web Service-Aufrufe der aktuellen Schnittstellen-Version im Einzelnen.
Siehe auch Überblick der Schnittstellen unter MagicMap Architektur und Schnittstellen
Inhaltsverzeichnis |
Schnittstellen-Versionierung und Kompatibilität
Die Web Service-Schnittstelle in MagicMap hat eine Versionsnummer. Aktuelle Version ist 1.0. Client- und Server-Schnittstelle müssen, um die Kompatibilität zu gewährleisten, immer die gleiche Schnittstellen-Version aufweisen. Dabei sind alle MagicMap-Client-Versionen mit einer Schnittstellen-Version kompatibel zu allen Server-Versionen mit der selben Schnittstellen-Version.
Bei Erweiterungen der Schnittstelle wird eine höhere Schnittstellen-Versionsnummer vergeben. Zur Abfrage der serverseitigen Schnittstellen-Version dient der Version Service. Stimmen die Versionen nicht überein, wird eine entsprechende Fehlermeldung ausgegeben.
In einigen Fällen kann ein Client trotzdem mit einem Server (eingeschränkt) zusammenarbeiten, obwohl die Schnittstellen Versionsnummern abweichen. Hierzu kann man die Fehlermeldung ignorieren und das Programm fortsetzen.
Zur Umsetzung der Web Services über SOAP wurde Apache Axis in der Version 1.2 verwendet. Bei Anbindung weiterer Clients an diese Server-Schnittstelle sollte also auch Axis 1.2 benutzt werden, da WSDL-Dateien, die mit Axis 2.0 generiert wurden nicht kompatibel sind und es zu Fehlern kommen kann.
Ab MagicMap Version 0.9.5 gibt es ein hierarchisches Kartenmodell und dementsprechend ein Update der Web Service Schnittstelle auf Version 1.2. Mehr dazu unter MagicMap Kartenmodell. Weitere Planungen unter MagicMapMilestones.
Beschreibung der Webservices (aktuelle Version 1.3)
Session Service
- sessionFacade: Ist zuständig für die Verbindung zum Server
- connect(string Name, string Mac, string Passwort, string Version)
- Erzeugt eine Verbindung zum Server
- Name ist der Name, mit dem der Client identifiziert wird
- Mac ist die Mac-Adresse des Clients (17 stelliger Wert wird erwartet, also die Zahlenpärchen mit "-" oder ":" getrennt)
- Passwort ist ein clientspezifisches Passwort
- Version ist die Version des Webservice (aktuell "1.3")
- Rückgabe der Methode ist die Session-ID (long), welche für viele der anderen Services benötigt wird
- disconnect (string Name, string Mac, string Passwort, long Session-ID)
- Trennt die Verbindung zum Server
- Name ist der Name, mit dem der Client identifiziert wird
- Mac ist die Mac-Adresse des Clients
- Passwort ist ein clientspezifisches Passwort
- Session-ID ist die ID, die der Client als Rückgabe auf den connect erhalten hat
Map Service
- mapFacade: Enthält die Funktionen um Karten zu erhalten, erstellen und Georeferenzpunkte auf diesen zu setzen und abzurufen
- getMapNames()
- Als Rückgabe erhält man die Namen aller verfügbaren Karten in einem Array des Types String[]
- Problem: Skaliert nicht. Besser mit getChildsOfObjects mit Parameterübeargabe (Object, Depth). Gibt alle Kinder des Objektes "Object" bis zur Tiefe "Depth" zurück. Bei einer Karte alle Child-Objekte in der Karte. Ist die Tiefe >1 rekursiev. Bei Tiefe 0 bis zu den Blättern. Dieser Aufruf ist nicht nur für Karten, sondern genauso auf Container oder andere Umverpackungen anwendbar.
- getMap (string Name)
- Name ist der Name, der anzufordernden Karte
- Rückgabe ist die Karte in Form eines MapDTO-Objektes (siehe komplexe Datentypen)
- createNewMap (long Session-ID, string Name, string URL, int Imagebreite, int Imagehöhe, int Breite, int Höhe, String ElternKartenName, String ElternKartenServerURL, String PositionAufElternKarte, Double PositionsFehler)
- erstellt eine Karte auf dem Server mit den definierten Parametern
- Session-ID ist die ID, die der Client beim Connect erhalten hat
- Name ist der der zu erzeugenden Karte
- URL ist die Internetadresse, auf welcher das Bild der Karte zu finden ist
- Imagebreite die Breite der Karte in Pixeln
- Imagehöhe die Höhe der Karte in Pixeln
- Breite derzeit unbenutzt, bitte auf 0 setzen
- Höhe derzeit unbenutzt, bitte auf 0 setzen
- ElternKartenName der Name der übergeordneten Karte in der Hierarchie
- ElternKartneServerURL die URL für den Server, der die Elternkarte verwaltet
- PositionAufElternKarte derzeit unbenutzt, bitte auf "" (leerer String) setzen
- PositionsFehler derzeit unbenutzt, bitte auf 0.0 setzen
- registerMapWithParent (MapInfoDTO KindKartenMapInfoDTO, String ElternKartenName)
- Registriert eine neue Kindkarte bei einer Elternkarte auf diesem Server, d.h. beim späteren Abrufen der Elternkarte erhält man alle Informationen, die man braucht um auch die Kindkarten abrufen zu können.
- KindKartenMapInfoDTO enthält den Namen, den Displaynamen und die Server URL zur neuen Kindkarte(siehe komplexe Datentypen)
- ElternKartenName Name der Elternkarte
- getMapInfosWithParent (String ElternKartenName)
- liefert zu einer gegebenen Karte, alle Kindkarteninformationen in Form von MapInfoDTO[] zurück.
- ElternKartenName der Name der Elternkarte
- changeMapParameters (long sessionId, String AlterName, String NeuerName, String URL, int Breite, int Höhe, int ImageBreite, int ImageHöhe, String ElternKartenName, String ElternKartenServerURL, String PositionAufElternKarte, Double PositionsFehler)
- überschreibt bei einer Karte alle Daten, die nicht auf null gesetzt wurden. Diese Methode dient Delevopementzwecken
- sessionId ist die ID, die der Client beim Connect erhalten hat
- AlterName ist der alte Name der Karte unter der sie auf dem Server gefunden werden kann
- NeuerName ist der neue Name der Karte unter dem sie zukünftig verfügbar ist. Das Ändern des Kartennamens wird zur Zeit nicht unterstützt
- URL ist die Internetadresse, auf welcher die Karte zu finden ist
- Breite Breite der Karte in Metern, wenn unbekannt, bitte auf 0 setzen
- Höhe der Karte in Metern, wenn unbekannt, bitte auf 0 setzen
- Imagebreite die Breite der Karte in Pixeln
- Imagehöhe die Höhe der Karte in Pixeln
- ElternKartenName der Name der übergeordneten Karte in der Hierarchie
- ElternKartenServerURL die URL für den Server, der die Elternkarte verwaltet
- PositionAufElternKarte derzeit unbenutzt, bitte auf null oder "" setzen
- PositionsFehler derzeit unbenutzt, bitte auf 0.0 setzen
PositionService
- positionFacade: Dient zum Abfragen und Eintragen von Positionen auf der Karte
- createOrUpdatePosition (long Session-ID, string Typ, string Name, int xPos, int yPos, int zPos, SignalCharacterDTO signals, string ID, string Displayname, bool fixed, AttributesDTO Attribute)
- Erstellt für ein Objekt eine Position auf der Karte, bzw. erneuert sie, falls bereits eine existiert. Der ParentLink des Objektes wird auf diese Karte gesetzt mit der Bedeutung, das sich das Objekt in der Karte befindet, bzw. allgemein untergeordnet zu dem Parent-Objekt. Siehe auch MagicMap_Knotenmodell#Objekt-Hierarchie_und_-Aggregation. D.h. mit getMap() lassen sich Kartendaten abfragen, aber erst durch createOrUpdatePosition wird die Karte "betreten".
- Session-ID ist die ID, die der Client beim Connect erhalten hat
- Typ des Objektes, welches erzeugt werden soll: "CLIENT", "ACCESSPOINT", "POSITION" (Referenzpunkt), "GEOPOSITION"
- Name ist der Name der aktuellen Karte
- xPos ist die x-Position des zu erzeugenden Objektes auf der Karte
- yPos ist die y-Position des zu erzeugenden Objektes auf der Karte
- zPos ist die z-Position des zu erzeugenden Objektes auf der Karte
- signals ist eine Array von Signalmessungen, (SignalCharacterDTO, welches als ein Attribut - ein Array von SimpleScanResultDTO's hat)
- ID ist die Mac-Adresse des Objektes
- Displayname gibt den darzustellenden Namen des Objektes an
- fixed gibt an, ob das Objekt beweglich ist
- Attributes enthält beliebige zusätzliche Informationen die benötigt werden könnten (etwa bei GPS Latitude, Longitude, ...)
- movePosition (long Session-ID, string Name, long Position-ID, int xPos, int yPos, int zPos, bool fixed)
- Verschiebt die Position eines Objektes auf der Karte
- Session-ID ist die ID, die der Client beim Connect erhalten hat
- Name ist der Name der aktuellen Karte
- Positions-ID ist Identifikation des Objektes das geändert werden soll
- xPos ist die neue x-Position des zu ändernden Objektes
- yPos ist die neue y-Position des zu ändernden Objektes
- zPos ist die neue z-Position des zu ändernden Objektes
- fixed gibt an, ob das Objekt beweglich ist
- deletePosition (long Session-ID, string Name, long Position-ID)
- Löscht die Position eines Objektes auf der Karte
- Session-ID ist die ID, die der Client beim Connect erhalten hat
- Name ist der Name der aktuellen Karte
- Positions-ID ist Identifikation des Objektes das gelöscht werden soll
- getPositionsForMapSince (long Session-ID, string[] types, string Name, string Timestamp, string Timeflag)
- Fragt alle Positionen der Objekte in einem bestimmten Zeitintervall ab.
- Session-ID ist die ID, die der Client beim Connect erhalten hat
- types gibt die Knotentypen an, für die man ein Update erhalten möchte ("CLIENT", "ACCESSPOINT", "POSITION"), bei Übergabe eines leeren Array werden alle Knoten übertragen
- Name ist der Name der aktuellen Karte
- Timestamp ist ein übergebener Zeitstempel, der abhängig vom
- Timeflag interpretiert wird: Mit gesetztem Flag wird von der aktuellen Zeit (in Millisekunden) der Wert des timeStamps abgezogen, er gibt also an wie weit man in die Vergangenheit blicken möchte. So kann man sich z.B. alle Positionen geben lassen, die in der letzten Stunde angelegt wurden. Aber vorsicht man bekommt damit wirklich _ALLE_ Änderungen. Wenn das timeflag auf false gesetzt wird, dann wird der timeStamp als Absolutwert interpretiert und man bekommt nur Positionsinformationen, die neuer als der timeStamp sind. Damit kann man dann deutlich Traffic sparen.
- Man macht also im Normalfall erstmal ein getPositionsForMapSince mit timeflag=true um möglichst viele Positionen zu bekommen und danach nur noch getPositionsForMapSince mit timeflag=false und dem timeStamp vom letzten getPositionsForMapSince.
- Rückgabe ist ein Array von PositionDTO-Objekten
- getPositionForClientOnMap (long Session-ID, string Name, string Mac)
- Fragt die Position eines bestimmten Clients auf der Karte ab und liefert dessen Position zurück
- Session-ID ist die ID, die der Client beim Connect erhalten hat
- Name ist der Name der aktuellen Karte
- Mac ist die Mac-Adresse des Klienten, dessen Position erfragt wird
- Diese Funktion wurde aus Performancegründen speziell für den CE Clienten geschrieben und gibt die Ergebnisse als String zurück.
- setAccessPointHiddenStatus (long Session-ID, string Name, string Mac, bool Status)
- Setzt das Sichtbarkeits-Attributs eines Accesspoints
- Session-ID ist die ID, die der Client beim Connect erhalten hat
- Name ist der Name der aktuellen Karte
- Mac ist die Mac-Adresse des Accesspoints, dessen Status geändert werden soll
- Status gibt an, ob der Status des Accesspoints auf versteckt gesetzt werden soll, oder nicht
- getPositionsWithKeysAndValuesForMap (long sessionId, String[] types, String mapName, AttributesDTO keyValueMap)
- Fragt Positionen beliebigen Typs auf einer oder allen Karten mit oder ohne bestimmten Attributen ab.
- Session-ID ist die ID, die der Client beim Connect erhalten hat
- trypes die Knotentypen, die man erfragen möchte, oder null bei beliebigen Knoten
- mapName der Kartenname auf der die Positionen sein sollen, oder null, wenn die Karte beliebig sein kann
- keyValueMap enthält beliebige zusätzliche Informationen die benötigt werden könnten (etwa bei GPS Latitude, Longitude, ...)
Version Service
- version: Diese WSDL dient zur Versionsabfrage der serverseitigen Web Service Schnittstelle.
- getVersion()
- gibt einen String zurück, der die Version des Apache-Servers und dessen Build-Datum enthält
Komplexe Datentypen
Im SessionFacade
SignalCharacterDTO
Ist eine Sammlungen von mehreren Signalmessungen und enthält als Variable ein Array von SimpleScanResultDTO's:
SignalCharacterDTO scDTO = new SignalCharacterDTO(); scDTO = ... scDTO.simpleScanResults[0] = ...
SimpleScanResultDTO
Ist die Signalmessung eines Clients von einem spezifischen (WLAN-)Accesspoint und enthält die Variablen identifier (string, identifiziert den AP), infos (vom Datentyp attributesDTO, für beliebige Informationen), lastSeen (long, Zeitpunkt der Messung), noise (double, wird derzeit nicht ausgewertet), signalLevel (double, empfangene Signalstärle), type (string, ...), ssid (string, die SSID des APs), technology (string, die Funktchnologie), variance (double, die Varianz der Signalstärken über die letzten 10 Messungen).
SimpleScanResultDTO ssrDTO = new SimpleScanResultDTO(); ssrDTO.identifier = ... ...
im PositionFacade
AttributeDTO
hat als Attribute zwei Arrays von Strings, diese müssen gleich lang sein. AttributeDTOs werden benutzt, um beliebige zusätzliche Informationen (bspw. bei GPS-Clients deren Längen- und Breitengrad) an den Server zu übermitteln bzw. zu erhalten, damit nicht für jeden Zweck neue Webservice-Methoden geschrieben werden müssen.
AttributesDTO aDTO = new AttributesDTO(); aDTO.keys = ... aDTO.values = ...
In der MapFacade
MapDTO
enthält diverse Informationen über eine MagicMap-Karte, dies sind: id (long, eindeutiger Identifizierer der Karte), imageURL (String, URL an der die Karte zu finden ist), name (String, name der Karte der in MagicMap angezeigt wird), imageHeight (int, x-Dimension der Karte), imageWidth (int, y-Dimension der Karte), height (int, reale x-Dimension), width (int, reale y-Dimension), parentName (String, Name der Elternkarte), parentServer (String, URL des Servers, der die Elternkarte verwaltet), positionOnParent (String, Position der Karte auf der Elternkarte, wird derzeit nicht benutzt), positionError(double, Positionsfehler auf der Elternkarte), activeClients (int, Anzahl der Clients die in den letzten 5 minuten auf der Karte aktv waren), attributes (AttributesDTO, Attribute der Karte).
MapDTO mDTO = new MapDTO(); mDTO.name = ... ....
MapInfoDTO
enthält Informationen zu einer Karte, die nötig sind um sie in der Kartenhierarchie anzuzeigen. Dazu gehören der Name, der Anzeigename und die URL des Servers, der diese Karte verwaltet.
Zu beachten
Visual Studio / c#
Webservices werden in Visual Studio folgendermaßen integriert: Rechter Mausklick auf das Projekt -> "Webverweis hinzufügen" -> im folgenden Fenster den Link zur entsprechenden WSDL-Seite eingeben -> Kurz warten, bis Webservice gefunden -> Klick auf "Verweis hinzufügen"
Die Webservices werden wie normale Variablen initialisiert:
private MapFacadeService mapService = new MapFacadeService(); private PositionFacadeService positionService = new PositionFacadeService(); private SessionFacadeService sessionService = new SessionFacadeService();
Der Namespache der benutzten Webservices muss in den Using-Directives stehen
using ...
(Namen werden von Visual Studio erzeugt)
Vor der Verwendung eines Webservices muss seine URL gesetzt (server ist hier die Adresse des Servers, auf dem der MagicMap Server läuft)...
private const string POSITION_URL = "/magicmap/services/PositionFacade?wsdl"; ... positionService.Url = "http://" + server + POSITION_URL;
und der Zeichensatz der Request gewählt werden (im Falle für WinCE / C#: ASCII).
mapService.RequestEncoding = System.Text.Encoding.ASCII;
Anschließend kann auf die Webservices wie auf Funktionen jeder anderen Klasse zugegriffen werden, etwa
positionService.createOrUpdatePosition(...);
sprachlich bedingte Stolperfalle
Die intuitive Benutzung der Datetime-Struktur von C# führt zu einem Fehler bei der Verarbeitung, da diese ab dem 01.01.0001 und nicht wie die in den Webservice verwendeten Unix-Zeit ab 1970 rechnet. Daher muss man den folgenden Umweg nutzen:
TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1)); long timestamp = (long) t.TotalSeconds;
Ein weiteres Problem bei der Nutzung der Webservices waren unterschiedliche Interpretationen der Sprachen von komplexen Datentypen. Die meisten Datentypen werden von Java und C# identisch interpretiert und ergeben keine Probleme.
Fehler traten aber bei der Nutzung von Hashmaps auf, diese sind in Java anders implenetiert als in C#, so dass die Webservices zwar problemlos aufgerufen werden können, entsprechende Variablen aber immer nur "null" enthalten. Als Lösung wurden die entsprechenden Hashmaps für die Übertragung durch etwas unelegantere Stringarrays substituiert, welche dann innerhalb des Programms wieder in Hashmaps umgewandelt werden können.
Sollten ähnliche Probleme bei der Nutzung unserer Webservices auftreten, kontaktieren Sie uns bitte.
WSDLs erstellen
Um aus den compilierten Javaklassen WSDLs zu generieren, benutzen wir Axis Version 1.2 in Verbindung mit Axis-Ant. Die Version dieser beiden Jars sind leider auf der offiziellen Seite nicht mehr verfügbar, aber in einem Archiv unter http://www.biojava.org/download/tools/jars-archive/ gibt es sie noch. Durch Axis-Ant kann man in der build.xml, dem Ant Skript, spezielle Axis Targets benutzen, die die Generierung von WSDLs aus Code bzw Code aus WSDLs übernehmen. Der folgende Code stammt aus der build.xml des MagicMap Servers und sorgt dort für die explizite Generierung der WSDLs.
<target name="wsdl" depends="jar" description="creates wsdl files for the client"> <path id="axis.classpath"> <fileset dir="dblayer/lib"> <-- hier liegen unsere Bibliotheken für die Datenbankanbindung --> <include name="**/*.jar" /> </fileset> <fileset dir="lib.dev"> <-- hier liegen unsere WebService Bibliotheken und auch die axis.jar und die axis-ant.jar --> <include name="**/*.jar" /> </fileset> <fileset dir="dist"> <-- hier liegen compilierten Javaklassen --> <include name="**/*.jar" /> </fileset> </path> <taskdef resource="axis-tasks.properties" classpathref="axis.classpath" /> <axis-java2wsdl classname="net.sf.magicmap.server.facade.MapFacade" namespace="http://localhost:8080/magicmap/services/MapFacade" location="http://localhost:8080/magicmap/services/MapFacade" typemappingversion="1.2" output="dist/MapFacade.wsdl"> <mapping namespace="http://exception.server.magicmap.sf.net" package="net.sf.magicmap.server.exception" /> <mapping namespace="urn:dto.server.magicmap.sf.net" package="net.sf.magicmap.server.dto" /> </axis-java2wsdl> <-- weitere axis-java2wsdl targets für andere WSDLs, der Aufbau bleibt aber gleich --> </target>
Nachdem der WSDL Task des Ant Skripts ausgeführt wurde, liegen dann im dist Ordner die fertigen WSDLs. Alternativ kann man die WSDLs auch direkt vom Tomcat erstellen lassen, was aber etwas mehr Konfigurationsarbeit vorraussetzt. (ToDo: alternativen Weg über Tomcat beschreiben)
Diese WSDLs kopiert man sich dann in das Clientprojekt und kann dann über axis-ant Tasks aus den WSDLs wieder Java generieren lassen. Im MagicMap Client Ant-Skript sieht der entsprechende Task wie folgt aus:
<target name="axis" depends="prepare">
<axis-wsdl2java output="${src.gen}" testcase="false" verbose="false" noimports="true" debug="false" url="file:${basedir}/inf/MapFacade.wsdl" />
<-- weitere axis-wsdl2java Aufrufe-->
</target>
Dabei wird aus der WSDL, die bei url angegeben ist, wieder Java Code erzeugt und in dem Ordner gespeichert, der unter output angegeben ist.
