Skip to content
PatrickTempel edited this page Jul 24, 2013 · 1 revision

Auszug aus einer Mail über Änderungen am MXJS-Core im neuen XSLT2JSClient:

Die Transformation kann wie üblich ohne zusätzliche Parameter gestartet werden. Die aktuelle Version generiert ausschließlich nicht minimiertes Javascript (also "gut" lesbar) und kann hoffentlich mit sämtlichen Fällen einer validen MSDL umgehen. Ich wollte mit der Transformation vor allem erreichen, dass der Nutzer überhaupt nicht mehr mit Strophe.js in Berührung kommt und nurnoch auf seinen, in der MSDL definierten, Objekten arbeiten muss. Zur Entwicklung des Generators habe ich einen einfachen Test-Service geschrieben, der die Grenzfälle testen sollte und nun auch den Funktionsumfang des Generators skizziert. Die entsprechende MSDL und Projekt-Dateien für den Service habe ich dir hier angehängt und werde anhand dieser MSDL im Folgenden beispielhaft die grundlegenden Ansätze erklären.

Erstellte Dateien:

  • out.xml-Datei mit Zusammenfassung (Welche Dateien wurden generiert)
  • mobilis/mobilis.js (die Original mobilis.js Datei => Aus eurem MXJS)
  • mobilis/mobilis.core.js (angepasster mobilis.core)
  • mobilis/mobilis.utils.js (neue utils Datei)
  • mobilis/mobilis.servicename.js (die generierte Service-Datei als "Plugin" für Mobilis)
  • servicename.js (eine Hilfsdatei)

Einige eventuell interessante Dinge zu den Dateien:

Der Mobilis Core (mobilis.core.js):

  • NS: mit den drei Namespaces/Idents vom Coordinator, Admin und Deployment, sowie dem Namespace für Error-Stanzas
  • Dieser NS für Error-Stanzas wäre eventuell ein Streitpunkt: Idee wäre hier, dass man den aus dem core wieder rausnimmt (Core soll eigentlich nur Schnittstelle zu Core-Services bieten) und anstelle in Mobilis.utils überführt (und Mobilis.utils eventuell in Mobilis.beans oder so umbenennt)
  • SERVICES: mit den gefundenen Services (Euer Ansatz den Mobilis-Server zu verteilen, finde ich super. Daher sind nun auch wieder die "jid" (als Full-JID) hier zu finden, also nicht die Agents wie ich aus "Teiloptimierungsgründen" bei unserem letzten Treffen genannt hatte => Der JS-Client kann somit theoretisch ohne weitere Anpassungen auch für diese neue Architektur genutzt werden)
  • EXCEPTIONS: Der Mobilis-Core wirft nun Exceptions, falls ein Service nicht gefunden werden kann oder noch keine Verbindung aufgebaut wurde (früher wurde das über einen errorcallback geregelt. Das empfinde ich allerdings nicht als wirklich sinnvoll, weil die errorcallbacks ausschließlich für die Rückgabe von Nutzdaten bei outfaults genutzt werden sollten und nicht mit Fehlern aus dem Core gemischt werden sollten... Das sind zwei unterschiedliche Aufgabenbereiche und führen nur auf Anwenderseite zu langen Standard-Checks zu Beginn jedes errorcallbacks)
  • Die Strophe.Status "enums" sind nicht mehr im Core, weil sie bereits von Strophe kommen.
  • connect: Es werden neue Parameter benötigt, um eine Verbindung aufzubauen:
  • uFullJid (FullJid vom Nutzer (alternativ auch eine BareJid): muss im XMPP-Server existieren)
  • uPassword (Passwort zur Jid vom Nutzer)
  • mBareJid (BareJid vom Mobilis-Server: Entspricht der BareJid zum Coordinator-Service)
  • bind (Früher war der HTTP-Bind direkt im core angegeben. Da dieser HTTP-Bind aber dem Bind vom XMPP-Server entsprechen muss und bei dem bisherigen MXJS HTTP-Bind auch ein Anonymous Login möglich ist, habe ich das entfernt und als explizite Angabe hinzugefügt: Dadurch kommt es hoffentlich zu weniger Missverständnissen der Art: "Hö? Er kann sich doch verbinden, aber mit der Bare/Full-Jid nicht authentifizieren? Die Angaben stimmen doch!" oder bei Angabe eines Nutzernamens anstelle einer BareJid (also sozusagen ohne Angabe des Service): "Hö? Er ist sogar "Connected", also hat die Authentifizierung auch geklappt... Aber wieso sendet er nichts? Der ist doch sogar verbunden!" => Ich persönlich sehe hier die größten Probleme für unbedarfte Entwickler, die einfach nach Trial and Error einen JS-Client aufsetzen wollen
  • onSuccess und onError: Werden nun anstelle von callback genutzt (Grund: Als Entwickler interessiert man sich hauptsächlich für diese beiden Fälle. Beim Callback musste man zusätzlich den Strophe.Status überprüfen, was ich sowieso verhindern wollte. Alles was darüber hinaus geht, wird von einem vermutlich versierterem Entwickler benötigt, der seine Anpassungen sowieso überall anbringen will, wo er sie gerade benötigt)
  • sendIQ habe ich vollständig angepasst. Grund: Der Core behandelt nun bei fehlendem onResult oder onError die IQs selbstständig (schlichte Ausgabe, analog zu dem bisherigen defaultcallback, defaullterrorback). Zusätzlich war die Funktion mit der Antwortzeit für ein IQ nett gemeint, aber so wie sie im MXJS-Core angegeben ist fehlerhaft (sobald ein Client zwei IQs sendet, bekommt er nicht mehr den Diff zu seiner Anfrage, sondern zu dem letzt-gesendeten. Was bei zwei aufeinanderfolgend gesendeten IQs entsprechend nicht der wirkliche Differenz entspricht => Hätte vermutlich über eine Art Decorator um den Defaultcallback gefixed werden können).
  • mobilisServiceDiscovery und createServiceInstance nutzen nun nicht mehr das Array! Ich finde für solche Dinge sind Arrays nicht so gut geeignet, weil die Zuordnung schlichtweg nicht so sauber ist Array[0] == ?. Es wird nun ein assoziatives Array/Objekt genutzt, dass anhand der Eigenschaften die entsprechende IQ aufbaut.

Die Mobilis-Utils (mobilis.utils.js):

  • Besitzt eine Reihe von Hilfsmethoden, die nicht im Core liegen, weil sie da irgendwie nach meinem Verständnis nicht hingehören (könnte auch als Beanutils durchgehen und mit dem NS für Error-Stanzas erweitert werden: siehe mobilis.core.NS)
  • trace: Dient zum einfachen tracen von Objekten und Nachrichten. (Wird von sämtlichen Core-Methoden genutzt, um etwas auf der Konsole auszugeben. Ist in dieser Version absolut rudimentär, aber teilweise sinnvoll, da man nun die "Mobilis: "-Nachrichten direkt von den anderen Ausgaben unterscheiden kann)
  • createMobilisServiceIq: Erstellt einen Strophe.Builder ($iq-Builder), der aufgrund des angegebenen Namespaces direkt die to-Adresse einträgt. (Kann nur genutzt werden, wenn bereits eine Verbindung besteht und somit der Mobilis.core die JID vom Service kennt. Ist auch ein Grund, warum die utils auch Beanutils genannt werden könnten)
  • getUnixTime: War so im Mobilis.core für die sendIQ mit defaultcallback (Wird von mir nicht benötigt, aber kann natürlich weiterhin genutzt werden)
  • appendElement: Hilfsfunktion, um ein Objekt (mit bekanntem Constructor(!), also: function ConstructorName() an einen Strophe-Builder anzuhängen -> Erzeugt die entsprechende Baumstruktur)

Die mobilis.-Service-Datei (mobilis.Servicename.js):

  • Wird entsprechend der Plugin-Idee aus MXJS als Mobilis.servicename an das Mobilis-Objekt angehängt. Es sind sämtliche Funktionen für einen JSClient aus der MSDL hier aufgeführt und können direkt genutzt werden. Der Aufbau ähnelt dem mobilis.xhunt.js und den Ideen aus dem JavaClient (War mir wichtig, damit ein Entwickler, der bislang nur das MSDL2JavaClient genutzt hat, auch direkt mit dem JSClient etwas anfangen kann). Im Gegensatz zu den anderen XSLT musste ich mich hier nicht auf einen "unbounded" Typ einigen: Der ist hier natürlich ein einfaches Array.
  • NS: Die Namespaces für den SERVICE und die Operationen (Ist für einen Entwickler, der nur den Service ansprechen will irrelevant)
  • ELEMENTS: Hier sind sowohl die ComplexTypes, als auch die Elemente definiert. Es werden alle ComplexTypes erstellt und nur die Elemente, die als Input/Output-Elemente genutzt werden (das entspricht dem Verhalten von MSDL2JavaClient und MSDL2JavaService). Es werden keine Outfault oder Infault-Elemente erzeugt (auch das entspricht diesem Verhalten). Ich habe es weiterhin möglich gelassen, dass manOut-Elemente instanziieren kann. Die Alternative wäre gewesen, dass man sich Code spart und dafür die Out-Elemente nur aus IQs bauen kann (nicht mehr aus den Parametern!). Das würde bedeuten, dass man zwar bei einem Output das Element im Client mit den entsprechenden Attributen erhält, aber nicht lokal dieses Element aufbauen kann, um damit zu arbeiten (was eventuell für manche Anwendungen nicht schön wäre).
  • ELEMENTS (Infaults): Die Grundidee aus MSDL2JavaService und MSDL2JavaClient habe ich hier beibehalten, obwohl der JSClient schon etwas weitergeht, als die Beans aus den MSDL2JavaService bzw. MSDL2JavaClient. Sollte eine Operation einen oder mehrere Infaults besitzen, wird dieser(werden diese) Infault(s) an das Nachrichten-Element angehängt und zugleich direkt als Methoden nutzbar gemacht:
  • Aus dem Bsp.: OutOnlyOperationWithFault Nutzt als Output ein OutElementFull und kann einen InFaultSimple schicken -> Das OutElementFull bekommt beim Instanziieren zusätzlich eine Methode: sendInFaultSimple (send + FaultName), die bereits darauf vorbereitet wurde diesen Fehler für diese IQ zu verschicken. Der Nutzer muss somit nur die Methode vom OutElementFull in seinem Listener/Handler von der Operation "OutOnlyOperationWithFault" nutzen: OutElementFull.sendInFaultSimple(); Zusätzlich kann er zu jedem Fehler optional eine Nachricht angeben: z.B. OutElementFull.sendInFaultSimple("Schick mal was anderes!"); Es können sämtliche Infaults bedient werden, die angegeben werden (es sind auch mehrere Infaults möglich). Die Idee entspricht somit weitestgehend der aus den anderen MSDL-XSLT (nur mit der Ausnahme, dass dort die Faults gebaut werden: OutElementFull.buildInFaultSimple(String detailedErrorText) und schließlich selbstständig verschickt werden müssen). Es gibt dennoch einen wesentlichen Unterschied zu dem Originalverhalten: Es werden auch Infaults generiert für das Message-Exchange-Pattern (mep) Out-Only. Ich sehe da keinen wesentlichen Widerspruch im Verhalten, da:
  • Out-Only: "to the consumer that may provide a status response"
  • Robust-Out-Only: " to which the consumer responds with status." Also es es ist durchaus möglich, dass der Client einen Fehler in Form eines Status liefern darf. Dieses Verhalten wurde allerdings nicht in den MSDL XSLT berücksichtigt (vermutlich liege ich auch einfach falsch, aber dann ist dieses Verhalten für den Client nicht schädlich, da mindestens der Service dann aufgrund der MSDL XSLT den Entwickler zur Nutzung von Out-In bei Nutzung von Infaults drängt.)
  • DECORATORS: Werden genutzt, um die Informationen aus den IQs zu extrahieren. Werden somit als eine Art programmatischer Intermediary genutzt, der sich zwischen den Callback/Listener und Strophe hängt und die Vorverarbeitung anstoßen. Die Callbacks geben je nach mep unterschiedliche Rückgaben zurück. An erster Stelle befindet sich stets das Kommunikationsobjekt (z.B. das OutElementFull). Bei OutIn-Operationen wird aber zusätzlich die packet-ID zurückgegeben. Es gibt zusätzlich die onError-Decorators, die bei Outfaults genutzt werden. Die Rückgabe entspricht dabei stets dem Schema: "Gesendetes Element, Error-Objekt { type, condition, message }". (Ist für einen Entwickler, der nur den Service ansprechen will irrelevant)
  • Operationen: Hier sind sämtliche Operationen angegeben, die für einen Entwickler, der nur mit dem Service kommunizieren will, relevant sind. Der Aufruf unterscheidet sich hier aber wesentlich bei den unterschiedlichen Operationen (Es ist nur sichergestellt, dass das Kommunikationsobjekt, stets der erste Parameter ist).
  • Am Bsp:
  • InOnlyOperation(InElement) -> Nur das Kommunikationsobjekt
  • InOnlyOperationWithFault(InElementFull, onError) -> Analog zu OutOnly (siehe Elements.Infaults) kann hier theoretisch ein Outfault kommen.
  • OutInOperationWithFault(InElementXS, packetID) -> Diese Operation geht davon aus, dass bereits ein Out-Nachricht kam und somit diese Methode aufgerufen werden sollte. Dafür ist natürlich die packetID relevant, die man auch beim Decorator zurückbekommt. Hier wäre der Streitpunkt: Sollte man das eventuell über einen Rückgabewert bereits im Decorator regeln (und das ganze eventuell analog zum Infault, also einer Art dynamischen Funktion, hier zurücksenden)? Ich finde es so für Javascript etwas sicherer, weil man nicht fordern kann, dass eine Rückgabe erfolgen muss. -> Leider besitzt dann ein Entwickler die Verantwortung :-/
  • InOutOperationWithFault(InElementXSSequence, onResult, onError, onTimeout) -> Hier kann alles passieren und man kann entsprechend für jede Eventualität einen Callback angeben. Der "onResult"-Callback erhält das Rückgabe-Kommunikationsobjekt (hier: OutElementXSSequence). Der "onError"-Callback erhält das Eingabe-Kommunikationsobjekt (hier: InElementXSSequence) und "onTimeout" wird schlicht aufgerufen, wenn keine Antwort kam.
  • add-Operationsname-Handler: Hier können Handler für sämtliche Funktionen registriert werden. Die Rückgaben sind Decorator-spezifisch, aber stets das erwartete Kommunikationsobjekt als Antwort. Es wird hier zusätzlich darauf geachtet, dass der xmpp:type eingehalten wird. Leider ist an dieser Stelle sowohl das Beispiel, als auch die Dokumentation von Mobilis fehlerhaft. Nach der Wiki auf Git-Hub soll es möglich sein, den xmpp:type "chat" anzugeben. Ich habe diesen natürlich auch in meinen Tests aufgeführt und musste feststellen, dass dieser bereits von der XMPPBean nicht unterstützt wird. Entsprechend wird im Beispiel der "addOutOnlyOperationHandler" niemals eine Nachricht erhalten, obwohl theoretisch eine Nachricht kommt (allerdings mit Type "get" -> Bei Änderung von diesem "chat" in "get" kommt auch hier eine Nachricht an).

Zuletzt noch die wahrscheinlich überflüssige servicename.js:

  • Ursprünglich sollte diese js einige Basisfunktionen bereitstellen. Ich denke, dass ich hier nochmal mit der Machete drübergehe und diese Datei in eine sample.servicename.js umarbeite. Das Ziel wäre demnach die Nutzung der generierten Funktionen anhand einer Art generierten Dokumentation (Code-basiert) für den JS-Client. Im Moment sind für dich vor allem interessant:
  • HTTPBIND: Hierher ist der HTTP-Bind verschwunden! Die Adresse ist auf jeden Fall nicht der Bind von Mobilis (aus oben genannten Gründen)
  • create-Methoden: Soll einfach zeigen, wie man mit den Objekten (Elemente, ComplexTypes) arbeiten kann
  • on-Operationsname-Methoden: Sind einfache Handler, aber hier fehlt z.B. bei onOutInOperationWithFault die packetID, die natürlich auch übergeben wird!
  • addHandlers: Die Methode kennst du ja bereits. Es wird allerdings nicht der onInOutOperationWithFault-Handler registriert (Grund: Man sollte ausschließlich den resultcallback nutzen!)
  • connect: Eine Hilfsmethode, um eine Verbindung aufzubauen. Hier könnte noch der onError zusätzlich angegeben werden.

Zusätzlich notwendige Skripte:

  • jQuery (ab Version ? Hab ich mir notiert, aber leider gerade an diesem Gerät nicht verfügbar... Tut mir Leid... Mit der neuesten Version fährst du auf jeden Fall sicher)
  • Strophe JS (ab Version 1.0.2: Ich kam bislang noch nicht dazu niedrigere Versionen zu testen)