Research and Development 1/^Archief/2009-2010/14/Pilotverslag

Uit Werkplaats
Ga naar: navigatie, zoeken
Bagjoke.jpg

Research and Development 1

Patrick van Bommel
Sjaak Smetsers


 © comments



  • Property "Auteur1" (as page type) with input value "  Research and Development 1/^Archief/2009-2010/14Gebruiker:Sjors Gielen" contains invalid characters or is incomplete and therefore can cause unexpected results during a query or annotation process.
  • Property "Auteur2" (as page type) with input value "  Research and Development 1/^Archief/2009-2010/14Gebruiker:Rob ten Berge" contains invalid characters or is incomplete and therefore can cause unexpected results during a query or annotation process.
  • Property "Auteur3" (as page type) with input value "  Research and Development 1/^Archief/2009-2010/14Gebruiker:Hans Harmannij" contains invalid characters or is incomplete and therefore can cause unexpected results during a query or annotation process.
  • Property "Auteur4" (as page type) with input value "  Research and Development 1/^Archief/2009-2010/14" contains invalid characters or is incomplete and therefore can cause unexpected results during a query or annotation process.

Inhoud

Over het project

Wij werken aan het project "RUWave", een Google Wave-server die uiteindelijk zal kunnen deelnemen in het Google Wave-netwerk. Ook zal onze server kunnen communiceren met de client van Project 5, en in samenwerking met die clients waves kunnen weergeven, veranderen en aanmaken.

Slides van de presentatie aan het eind van de pilot zijn hier te bekijken.

De pilot

In de pilot hebben wij ons gericht op het uitzoeken hoe het de server communiceert met andere servers en clients. Ook hebben we uitgezocht, beredeneerd en onderzocht wat een server intern allemaal moet verwerken, en hoe wij dat gaan implementeren.

In het begin

Dit is wat wij in het begin opschreven over onze doelen in de pilot

  • Hoe werkt het protocol tot nu toe? Wat mist er, en wat is daarover al afgesproken op de forums van Google Wave? Wat kunnen wij zelf nog toevoegen? Wat zijn de struikelblokken, en dergelijke?
  • Hoe moet de RUWave-server communiceren met de clients? Dit gaat in overleg met het andere groepje. We bedenken daarbij waarschijnlijk een protocol.
  • Wat moet de RUWave-server intern allemaal kunnen, op basis van wat nu bekend is over de protocols? Wat zijn daar belangrijke struikelblokken?
  • Hoe kunnen we de componenten het beste aan elkaar koppelen? Welke componenten hebben toegang nodig tot het protocol, en dergelijke?

Als product van de eerste vraag willen we een soort "samenvatting" van de rode draad van het protocol. Aan de hand daarvan willen we het gemakkelijk kunnen integreren met de rest. Daarbij willen we op een of andere manier extra informatie vanuit die samenvatting kunnen vinden, bijv. linkjes naar het forum of andere documentatie.

Voor de tweede vraag gaan we in overleg met het andere groepje. Daaruit willen we uiteindelijk een document dat ons client-serverprotocol (of een al bestaand protocol, tot hun discretie) beschrijft.

Als product van de derde vraag willen we iets vergelijkbaars: een lijst van componenten die los van elkaar hun werk kunnen doen.

Op de vierde vraag willen we een diagram waarop te zien is hoe alle losse componenten samenwerken. Op basis daarvan kunnen we, als het goed is, een goede taakverdeling maken om in de eerste fase snel te kunnen beginnen.

Wat we bereikt hebben

Het klassediagram

Wij hebben uitgezocht hoe het Federatieprotocol werkt. Het gebruikt het XMPP-protocol met de PubSub-extensies, waarover in dit verslag later meer. Daarnaast hebben wij documentatie over het Federatieprotocol gelezen en daarover hebben wij in dit verslag ook meer informatie ingevoegd. We hebben ons hierbij wel gelimiteerd tot de Eerste Milestone, het "realtime" kunnen uitwisselen van read-only gegevens met de client. Dat wil zeggen dat de server in het netwerk moet kunnen plaatsnemen, Waves moet kunnen ontvangen, naar de clients moet kunnen sturen, en daarbij updates ook weer naar de clients moet kunnen sturen. Zelf wijzigen of zelf aanmaken hebben wij nog niet opgenomen.

Voor die milestone hebben wij dus ook in gesprek met het andere groepje een client-serverprotocol proberen op te stellen. Het andere groepje is vooral bezig geweest met de exacte specificatie daarvan, omdat wij dat in overleg zo ingedeeld hadden. In hun verslag staat daar dus meer informatie over, als het goed is.

We hebben een schema gemaakt van onderdelen van de server, dat schema is ook te zien op de derde slide van de post-pilotpresentatie. Onderdelen in het rood geschreven gaan wij maken, onderdelen in het groen het clientgroepje. Daarbij hebben we een aantal onderdelen die zowel in de client als in de server gebruikt worden, in een "common"-gedeelte geplaatst, zodat we het niet 2x hoeven te schrijven. (Het client-serverprotocol ontbreekt in het common-gedeelte, omdat dat door de server en de client significant anders geïmplementeerd gaat worden.)

Over het gedeelte van Operational Transformation staat in dit verslag ook veel informatie. Hierbij hebben we al interfaces en een klassediagram, hier rechts te zien. Voor de rest gaan we de interfaces in de loop van fase 1 maken.

Het logboek van de pilot is hier te zien.

XMPP Core (RFC 3920)

Korte samenvatting

Het Extensible Messaging and Presence Protocol (XMPP) is een protocol dat gebruikt maakt van XML om o.a. near-realtime instant messaging, presence en request-response services mogelijk te maken. Daarnaast is het gemaakt om uitbreidbaar te zijn met Extensions, waar Google Wave een voorbeeld van is.

Overview

XMPP wordt tot dusver geimplementeerd met TCP verbindingen, al is dat niet een vereiste. Een overzicht van het soort XMPP verbindingen is bijvoorbeeld deze:

C1----S1---S2---C3
      |
C2----+--G1===FN1===FC1
  • C1, C2, C3 zijn XMPP-clients;
  • S1, S2 zijn XMPP-servers;
  • G1 is een gateway die vertaalt tussen XMPP en een extern (niet XMPP) messaging protocol;
  • FN1 is een extern messaging protocol;
  • FC1 is een client op een extern messaging-netwerk.

Een XMPP-server dient om XML streams van anderen op het XMPP netwerk te ontvangen en door te sturen indien zo beschreven door de ontvangen XML-stanza(s) in de XML-stream. Een XMPP-client verbindt over het algemeen direct met een XMPP-server via TCP, om dan het XMPP-protocol te gebruiken. Een gateway is een speciale XMPP-server die primair bedoeld is om berichten over het XMPP-protocol te vertalen naar een ander protocol en omgekeerd.

Adressering

Een object is alles wat beschouwd kan worden als een eindpunt in het XMPP netwerk. Al zulke objecten zijn uniek identificieerbaar door een zogenoemde jabber-id of jid.

Een jid is als volgt opgebouwd:

   jid             = [ node "@" ] domain [ "/" resource ]
   domain          = fqdn / address-literal
   fqdn            = (sub-domain 1*("." sub-domain))
   sub-domain      = (internationalized domain label)
   address-literal = IPv4address / IPv6address

waar [] voor optioneel staat, / voor keuze (a = b / c, dan is a óf b óf c), en 1* voor repetitie staat van minstens één keer. Het enige verplichte element is het deel domain. Domain moet een volledige domeinnaam zijn. Node staat over het algemeen voor een client die verbindt met een server. Resource wordt normaal gesproken gebruikt om een specifiek deel van de communicatie tussen een client en server te identificeren.

XML Streams

XMPP-peers praten met elkaar via XML-streams. Een XML-stream is een container voor de uitwisseling van XML-elementen tussen twee objecten in het XMPP-netwerk. Het begin van een XML-stream is altijd het openen van een <stream> tag, met bijbehorende attributen en namespace definities. Een enkele XML-stream is eenrichtingsverkeer, dus een verbinding die beide kanten op gaat, bestaat uit twee XML-streams.

Definitie van een XML-stanza

Een XML stanza is een XML element dat een directe node is van het <stream> element. Binnen dit XML element mogen weer andere XML element staan waar nodig. Dus bijv.:

<stream>
  <presence>
    <show/>
  </presence>
  <message to="bla">
    <body/>
  </message>
  ...
</stream>

In dit voorbeeld zijn presence en message twee XML-stanzas binnen een XML-stream. Wanneer een XML-stream gesloten wordt door </stream> te sturen, hoort de onderliggende TCP-verbinding ook gesloten te worden.

In de context client-naar-servercommunicatie gebruiken client en server een enkele TCP-verbinding beide kanten op, dus er zijn dan twee XML-streams. Voor server-naar-serververkeer moet een server een TCP-verbinding gebruiken voor data van de server af, en een aparte TCP-verbinding voor data naar de server toe, dus twee TCP-verbindingen totaal.

Attributen van het Stream-element

  • to: bedoeld voor initiatie van een XML stream, de inhoud moet een domein zijn horende bij de ontvangende partij.
  • from: bedoeld als antwoord op een initiatie van een XML stream, de inhoud moet gelijk zijn aan het domein van de partij die de XML stream geinitieerd heeft.
  • id: alleen te gebruiken in de XML stream header van de ontvangende partij. dit id is een unieke identificatie voor de geinitialiseerde sessie tussen de twee partijen
  • xml:lang: bedoeld voor de initialiserende partij om de standaardtaal voor menselijk leesbare data verstuurd over de XML stream aan te geven. Indien afwezig, wordt de standaardtaal bij de ontvangende partij aangenomen.
  • version: geeft aan welke XMPP versie gebruikt wordt door een partij, moet minstens "1.0" zijn.

Het gebruik van TLS

Transport Layer Security (TLS) is een methode voor het beveiligen van een XML-stream. Bij het initialiseren van een XML-stream moet vastgesteld worden of een XML-stream door TLS beveiligd zal worden, en slechts in het geval dat beide partijen in staat zijn TLS toe te passen. Dit wordt uitgelegd door de extensie STARTTLS, dat gebruikmaakt van de XML-stanza starttls.

het gebruikt van SASL

Simple Authentication and Security Later (SASL) is een methode om XML-streams te authenticeren binnen het XMPP-netwerk. Dit houdt in dat de initialiserende en ontvangende partij over een gebruikte authenticatiemethode onderhandelen tot een gemeenschappelijke gevonden wordt. Daarna worden door middel van die methode beide partijen geauthenticeerd, en daarna wordt een nieuwe XML-stream begonnen.

Resource Binding

Wanneer een SASL-onderhandeling met succes is afgerond, moet de server een stream header sturen met een lijst van beschikbare features. In het bijzonder, wanneer een server vereist dat een client aan resource binding moet doen, dan moet er een XML-stanza verstuurd worden in deze vorm:

<stream:features>
  <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
</stream:features>

Indien de client toestaat dat de server de resource identifier genereert, stuurt de client deze XML stanza:

<iq type='set' id='bind_1'>
  <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
</iq>

Indien de client zelf de resource identified genereert, stuurt de client een andere XML stanza:

<iq type='set' id='bind_2'>
  <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>
    <resource>someresource</resource>
  </bind>
</iq>

In beide gevallen moet de server een stanza terugsturen dat de resource binding gelukt is, waarin een complete jid staat:

<iq type='result' id='bind_2'>
  <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>
    <jid>somenode@example.com/someresource</jid>
  </bind>
</iq>

Ook mag een server een door de client gegenereerde resource identifier overschrijven, maar moet dan nog steeds bovenstaande stanza sturen. Daarnaast zijn er stanzas voor foutmeldingen.

Server Dialback

Server Dialback (SD) is een methode om te beschermen tegen domain spoofing, zodat het moeilijker wordt neppe XML-stanzas in te voegen in een stream. SD is een XMPP-feature voornamelijk wegens backwards compatibility; wanneer SASL beschikbaar is moet dat in plaats van SD gebruikt worden.

SD kan slechts een eenrichtingsverkeerstream authenticeren, dus voor volledige authenticatie moet de SD-check apart uitgevoerd worden voor de ontvangende stream en de versturende stream. SD werkt door middel van DNS, omdat een server normaal gesproken een autoritaire server kan vinden voor een gegeven domeinnaam.

De methode:

  • 1. De versturende server maakt een verbinding met de ontvangende server.
  • 2. De versturende server stuurt een 'key'-waarde over de verbinding naar de ontvangende server.
  • 3. De ontvangende server maakt een verbinding met de autoritaire server.
  • 4. De ontvangende server stuurt de ontvangen 'key'-waarde naar de autoritaire server.
  • 5. De autoritaire server stuurt terug of de 'key'-waarde wel of niet geldig is.
  • 6. De ontvangende server stuurt de versturende server een bericht of deze wel of niet geauthenticiteerd is.

XML Stanzas

In deze RFC worden drie stanzas gedefinieerd voor de 'jabber:client'- en 'jabber:server'-namespaces: <message/>, <presence/> en <iq/>. Deze hebben vijf overeenkomstige attributen.

  • to specificeert de jid van de bedoelde ontvanger voor een stanza;
  • from specificeert de jid vam de zender;
  • id specificeert een id voor het intern volgen van een stanza door de verzendende partij;
  • type specificeert informatie over het doel van een stanza, de toegestane waardes verschillen per stanza;
  • xml:lang specificeert de taal waarin menselijk leesbare data wordt geïnterpreteerd voor een stanza.

message

Deze stanza kan gezien worden als een soort 'push' mechanisme, waar een object informatie naar een ander object duwt.

presence

Het presence stanza kan gezien worden als "publish-subscribe" mechanisme, waar meerdere objecten informatie ontvangen van een object waartoe ze zich hebben aangemeld.

iq

Iq staat hier voor Info/Query, een stanza voor een 'request-response'-mechanisme. Voor een iq stanza is het id-attribuut verplicht, en is ook het type-attribuut verplicht. Het type-attribuut kan de volgende waarden hebben, met de volgende eisen:

  • get: een object dat een iq stanza met een get type attribuut ontvangt moet beantwoorden met een iq stanza met dezelfde id en of het type result of het type error.
  • set: zelfde als get
  • result: geen, de stanza is het resultaat van een succesvol get of set iq stanza.
  • error: de get of set iq-stanza die een error veroorzaakt hoort als element in de error stanza voor te komen, samen met een <error/> element.

Enkele serverregels voor het verwerken van XML-stanzas =

Wanneer een stanza geen 'to'-attribuut bevat, moet de server deze namens het object dat het verstuurd heeft verwerken. Bij een presence-stanza zonder 'to' attribuut hoort een server deze door te sturen naar alle object die zich hebben aangemeld bij de verstuurder van die stanza. Als de hostname van het domain identifier-deel van de jabber-id in een 'to'-attribuut niet overeenkomt met de geconfigureerde hostnames van de server zelf, hoort de server deze door te sturen naar de onbekende hostname. Wanneer dit niet mogelijk is, stuurt de server een foutmelding terug.

Gebruik van XML binnen XMPP

Hoewel XMPP gebruikt maakt van XML, is het niet mogelijk de volledige verzameling van features in XML te ondersteunen. In het specifiek gelden deze beperkingen:

  • Comments zijn niet toegstaan.
  • Verwerkingsinstructies zijn niet toegstaan.
  • Interne of externe DTD-deelverzamelingen zijn niet toegestaan.
  • Voor interne of externe verwijzingen naar objecten zijn alleen voorgedefinieerde objecten toegestaan.
  • Karakter data of attribuut waardes zonder 'unescaped characters'. zijn niet toegestaan.

Het definiereren van een standaardnamespace is vereist en moet precies gelijk zijn aan "http://etherx.jabber.org/streams".

Minimale vereiste implementatie

Serverkant

  • Applicatie van de NAMEPREP, Nodeprep en Resourceprep profielen
  • XML streams, inclusief gebruik van TLS, SASL en Resource Binding
  • De basis semantiek betreffende de drie stanzas in dit document gespecificeerd (<message/>, <presence/>, <iq/>)
  • Genereren van error syntax en semantiek gerelateerd aan streams, TLS, SASL en XML stanzas

Clientkant

  • XML streams, inclusief gebruik van TLS, SASL en Resource Binding
  • De basis semantiek betreffende de drie stanzas in dit document gespecificeerd (<message/>, <presence/>, <iq/>)
  • Verwerken (danwel genereren) van error syntax en semantiek gerelateerd aan streams, TLS, SASL en XML-stanzas

Volgorde van lagen binnen XMPP

De volgorde moet als volgt zijn:

  • 1. TCP
  • 2. TLS
  • 3. SASL
  • 4. XMPP

XMPP Publish Subscribe

De XMPP PubSub extension is bedoeld als framework voor systemen die het 'publish-subscribe' mechanisme gebruiken. Dat houdt in dat als een persoon of applicatie informatie publiceert, dan wordt een notificatie (met of zonder volledige inhoud) verzonden naar alle leden. De stanzas voor elke use case kunnen in de documentatie gevonden worden.

Entity Use Case

Discover Features

De zender vraagt welke features ondersteund zijn door object, waarop een stanza wordt teruggestuurd met de ondersteunde PubSub features.

Discover Nodes

De zender vraagt object welke nodes beschikbaar zijn, en krijgt als antwoord alle nodes van het gevraagde niveau.

Discover Node Information

De zender vraagt object om specifieke informatie over een node, waarop gereageerd wordt met het type van de node, welke 'leaf' of 'collection' kan zijn.

Discover Node Meta-Data

De zender vraagt object om meta-data van een node. De ontvanger stuurt terug een lijst met meta-data zoals 'creation date', 'creator', 'type', etc.

Discover Items for a Node

Object wordt gevraagd om informatie over gepubliceerde items binnen een node terug te sturen.

Retrieve Subscriptions

De zender vraagt object informatie over alle actieve abonnementen van de zender terug te sturen.

Retrieve Affiliations

De zender vraagt object tot welke groep zender behoort voor elke node beschikbaar op object.


Subscriber Use Cases

Subscribe to a Node

Hiermee doet de zender een poging zich te abonneren op een node op object. Het antwoord is of 'succes' of een foutmelding.

Unsubscribe from a Node

De zender verstuurt een bericht naar object dat de zender een abonnement een op een opzegt. Object reageert terug met een bericht of de opzegging succesvol is verlopen.

Configure Subscription Options

De zender vraagt object om de huidige instellingen of verstuurt instellingen naar object zodat deze opgeslagen kunnen worden. Object stuurt een bericht terug of alles correct was en of het opslaan correct is verlopen. Voor deze use-case wordt het gebruik van het Data Forms protocol aangeraden.

Retrieve Items from a Node

De zender vraagt object om één of meer items binnen een node, met als voorwaarde dat de zender zich in de juiste groep bevindt. De zender kan vragen om alle items, waarna object of alle items terugstuurt, of een aantal items met een melding dat er items zijn weggelaten. De zender kan ook vragen om slechts notificaties te ontvangen.

Publisher Use Cases

Publish an Item to a Node

De zender stuurt een bericht naar object om een item in een node te publiceren. Object stuurt terug of dit succesvol is verlopen.

Delete an Item from a Node

De zender verzoekt aan object een specifiek item binnen een node te verwijderen.

Federatie- en clientserverprotocol

Het Federatieprotocol

Het federatieprotocol is het protocol dat alle servers op het Google Wave-netwerk met elkaar spreken. Wij gaan dit protocol implementeren als server en als client, wat wil zeggen dat wij naar andere servers kunnen verbinden en andere servers ook naar ons. Het Federation-protocol is nog constant in ontwikkeling, het kan dus zijn dat de informatie die wij hieronder aangeven nog gaat veranderen of al veranderd is in de huidige implementaties. Dat zullen we merken terwijl we de code schrijven en de server steeds meer deelneemt in het netwerk.

Het federatieprotocol bouwt niet alleen voort op XMPP zoals XMPP voortbouwt op TCP, namelijk door van de "open plekken" gebruik te maken die in dat protocol gespecificeerd zijn. Daarnaast zorgt XMPP ook voor features waar het federatieprotocol gebruik van gaat maken. Zo heeft XMPP voor ons al authenticatie, een standaard voor inloggen, en iets dat je service discovery noemt: zoals iedereen door middel van het DNS MX-record de mailservers van een domein kan uitzoeken, maakt XMP gebruik van het DNS SRV-record, dat ook al voor dingen als Jabber (ook XMPP) en SIP wordt gebruikt.

Het Federation-protocol verplicht een al beveiligde XMPP-stream en specificeert verder alleen nog verschillende stanza's om te versturen. Denk er hierbij aan dat de server aan de andere kant géén antwoorden kan sturen over dezelfde verbinding; als een server berichten terug wil sturen moet hij een nieuwe verbinding openen naar de juiste server, daarop een XML-stream openen, authenticeren zoals het protocol voorschrijft, en dan kunnen er berichten overgestuurd worden.

Waves en wavelets, hosts en remotes

Elke Wave wordt gehost op een Federation-server, die server heet de "Federation-host". Een Wave heeft verschillende Wavelets, die ook allemaal een eigen host hebben; die host kan dezelfde host zijn als van de wave. Dat geldt bijvoorbeeld voor de "hoofd-wavelet" in een wave. Voor een Wave-verandering moet een Wave-remote de verandering naar de Wave-host sturen (tenzij dat dezelfde server is); de Wave-host moet vervolgens die verandering ook naar alle remotes sturen.

Hoe een Wave verder is opgebouwd, is verder niet zo belangrijk voor het federatieprotocol zélf, maar ik zal het even kort toelichten: de Wavelets in een Wave bestaan allemaal uit documenten. Je hebt "onzichtbare" documenten zoals tags en wie de wave kunnen bekijken (de participant list) en "zichtbare" documenten zoals de blips: de werkelijke stukjes tekst in een wave.

Stanza's, attributen en elementen

Het Federation-protocol schrijft verschillende bruikbare stanza's voor. Hierbij moet in acht worden genomen dat het protocol onder ontwikkeling en niet compleet is. De stanza's definiëren niet alles; wij zullen er in de loop van fase 1 achter moeten komen hoe het allemaal werkelijk geïmplementeerd is. De specificatie geeft bijvoorbeeld ook vaak een aantal voorbeelden, maar generaliseert niet wat een server of een client precies moet doen, of terug kan sturen. De voorbeelden die in dit verslag staan, zijn overgenomen uit de specificaties, zie ook het kopje "Bronnen" aan het eind van dit document.

Wavelet update

Het Federation-protocol definieert een "Wavelet update", waarmee Federation-hosts updates die aan een wave gedaan zijn, naar alle Federation-remotes kunnen sturen. Als een Federation remote een wijziging aan een Wavelet wil doen, gebruikt hij overigens niet dit commando; zie voor het juiste commando Operatie invoeren hieronder.

<message type="normal"
  from="wave.initech-corp.com"
  id="1-1" to="wave.acmewave.com">
  <request xmlns="urn:xmpp:receipts"/>
  <event xmlns="http://jabber.org/protocol/pubsub#event">
    <items>
      <item>
        <wavelet-update
          xmlns="http://waveprotocol.org/protocol/0.2/waveserver"
          wavelet-name="acmewave.com/initech-corp.com!a/b">
          <applied-delta><![CDATA[CiI...MwE] ]></applied-delta>
        </wavelet-update>
      </item>
    </items>
  </event>
</message>

De federation remote stuurt daarna een antwoord dat alles gelukt is: (via een andere verbinding, maar dat is even niet van belang)

<message id="1-1"
  from="wave.acmewave.com"
  to="wave.initech-corp.com">
  <received
    xmlns="urn:xmpp:receipts"/>
</message>

History retrieval

Een federation remote, die geen history bijhoudt, kan een request sturen voor die history in een PubSub-context, bijvoorbeeld:

<iq type="get" id="1-1" from="wave.initech-corp.com" to="wave.acmewave.com">
  <pubsub xmlns="http://jabber.org/protocol/pubsub">
    <items node="wavelet">
      <delta-history
        xmlns="http://waveprotocol.org/protocol/0.2/waveserver"
        start-version="12"
        start-version-hash=""
        end-version="2345"
        end-version-hash=""
        response-length-limit="300000"
        wavelet-name="acmewave.com/initech-corp.com!a/b"/>
    </items>
  </pubsub>
</iq>

Daarop krijgt de server dan een antwoord terug van de host:

<iq type="result" id="1-1" from="wave.acmewave.com" to="wave.initech-corp.com">
  <pubsub xmlns="http://jabber.org/protocol/pubsub">
    <items>
      <item>
        <applied-delta
          xmlns="http://waveprotocol.org/protocol/0.2/waveserver">
            <![CDATA[CiI...MwE] ]>
        </applied-delta>
      </item>
      <item>
        <commit-notice
          xmlns="http://waveprotocol.org/protocol/0.2/waveserver"
          version="2344"/>
      </item>
      <item>
        <history-truncated
          xmlns="http://waveprotocol.org/protocol/0.2/waveserver"
          version="2300"/>
      </item>
    </items>
  </pubsub>
</iq>

Operatie invoeren

Als een federation remote een verandering wil opsturen naar de federation host, gebruikt hij het Submit Request-stanza, met daarin een operatie:

<iq type="set" id="1-1" from="wave.initech-corp.com" to="wave.acmewave.com">
  <pubsub xmlns="http://jabber.org/protocol/pubsub">
    <publish node="wavelet">
      <item>
        <submit-request
          xmlns="http://waveprotocol.org/protocol/0.2/waveserver">
          <delta wavelet-name="acmewave.com/initech-corp.com!a/b">
            <![CDATA[CiA...NvbQ==] ]>
          </delta>
        </submit-request>
      </item>
    </publish>
  </pubsub>
</iq>

Daarna stuurt de federation host een bericht terug met de updates die hij heeft doorgevoerd:

<iq type="result" id="1-1" from="wave.acmewave.com" to="wave.initech-corp.com">
  <pubsub xmlns="http://jabber.org/protocol/pubsub">
    <publish>
      <item>
        <submit-response
          xmlns="http://waveprotocol.org/protocol/0.2/waveserver"
          application-timestamp="1234567890"
          operations-applied="2">
          <hashed-version
            history-hash=""
            version="1234"/>
        </submit-response>
      </item>
    </publish>
  </pubsub>
</iq>

Live updates

Wanneer een federation host aan al zijn federation remotes een update wil sturen, uit zichzelf, ziet dat er zo uit:

<message type="normal" from="wave.initech-corp.com" id="1-1" to="wave.acmewave.com">
  <request xmlns="urn:xmpp:receipts"/>
  <event xmlns="http://jabber.org/protocol/pubsub#event">
    <items>
      <item>
        <wavelet-update xmlns="http://waveprotocol.org/protocol/0.2/waveserver" wavelet-name="acmewave.com/initech-corp.com!a/b">
          <applied-delta><![CDATA[CiIKIAoFCNIJEgASF2ZvenppZUBpbml0ZWNoLWNvcnAuY29tEgUI0gkSABgCINKF2MwE] ]></applied-delta>
        </wavelet-update>
      </item>
    </items>
  </event>
</message>

Waarna de wave-remote antwoordt met een simpel "ik heb je bericht ontvangen":

<message id="1-1" from="wave.acmewave.com" to="wave.initech-corp.com">
  <received xmlns="urn:xmpp:receipts"/>
</message>

Signature requests en submits

In dit verslag heb ik informatie over signatures even weggelaten. Cryptografische signatures bestaan in Google Wave om te verifiëren dat een bepaalde verandering inderdaad van een specifieke gebruiker komt, in plaats van dat een kwaadwillende Wave-provider kan zeggen dat veranderingen van willekeurige users komen.

Over het controleren van signatures gaan wij pas in fase 1 nadenken. Daarom ga ik in dit verslag nog geen informatie opgenomen over dit onderdeel van het Wave-protocol.

Het clientserverprotocol

Wij implementeren het clientserverprotocol aan de serverkant. Meerdere clients verbinden naar onze server, en spreken dit protocol ook; zo zullen wij informatie uitwisselen met de clients.

Het clientserverprotocol wordt voor een groot deel vastgesteld door het clientgroepje. Wel hebben wij als servergroepje overleg gepleegd met het clientgroepje over dit onderwerp. We hebben technodrama's gehouden om erachter te komen wat er precies heen en weer gestuurd zou moeten worden, en we hebben uitgezocht hoe XMPP werkt (waar ook het clientserverprotocol op voortbouwt). De leden van het clientgroepje hebben in hun verslag meer informatie staan over het clientserverprotocol, daar besteden wij hier verder geen aandacht meer aan.

Operational transformation

Operaties

Een wavelet is opgebouwd uit operaties. Dit zijn meestal documentoperaties die op de inhoud van een wavelet werken, maar ook operaties die bijvoorbeeld deelnemers toevoegen of verwijderen. Een documentoperatie is opgebouwd uit mutaties. Een documentoperatie kan dus meer dan één mutatie bevatten. Operaties kunnen aan elkaar gekoppeld worden tot grotere operaties. Hierdoor zijn er minder operaties nodig voor een complete wavelet. Doordat een wavelet opgebouwd is uit operaties kan er makkelijk teruggedraaid worden naar een eerdere versie. De volgende mutaties voor documenten zijn er:

  • skip
  • insert characters
  • insert element start
  • insert element end
  • insert anti-element start
  • insert anti-element end
  • delete characters
  • delete element start
  • delete element end
  • delete anti-element start
  • delete anti-element end
  • set attributes
  • update attributes
  • commence annotation
  • conclude annotation

Aan mutaties zelf worden geen posities meegegeven om aan te geven waar de mutatie plaats moet vinden, de positie wordt bepaald met de skipmutatie. Unicode-tekens en XML-tags zijn 1 item, en nemen dus 1 positie in. Het eerste item staat op positie 0. Operaties worden verstuurd in XML-documenten via het XMPP-protocol naar andere servers. Hoe die XML-documenten eruit zien staat in de specificatie. Hierin worden de operaties zelf met Base64 gecodeerd.

Transformation

Als een wavelet door meerdere mensen wordt gewijzigd moet er voor gezorgd worden dat ondanks de vertragingen die er door o.a. de internetverbinding zijn gezorgd worden dat de operaties in de juiste volgorde en op de juiste positie uitgevoerd worden. Dit is Operational Transformation. Google Wave gebruikt hiervoor het al bestaande Jupiter-systeem, met wat aanpassingen.

Een voorbeeld van een geslaagde operatie

Als er ergens bijvoorbeeld een teken wordt verwijderd, moeten van alle operaties verderop in de wavelet de posities met 1 worden verminderd om de operatie op de juiste positie plaats te laten vinden. Een voorbeeld is rechts te zien.

Hiervoor is het nodig dat de server weet wanneer een operatie werd uitgevoerd. Hiervoor houdt de wavelet zijn huidige versie bij, en krijgt elke operatie het versienummer van de wavelet op het moment voordat die operatie wordt uitgevoerd mee. Na het uitvoeren van operaties wordt het versienummer opgehoogd.

Google Wave voegt aan het al bestaande Jupiter-systeem toe, dat de client vervolgens wacht op een bevestiging van de server voordat de volgende operatie wordt gestuurd. Dit gebeurt omdat dat voordelen biedt als er met veel mensen samengewerkt wordt. Als een client direct weer nieuwe operaties kan sturen, en de server ook, dan doorlopen client en server, afhankelijk van wanneer ze bepaalde operaties ontvangen, een heel verschillend pad door de transformaties:

OTStateSpace.png

De server moet deze state-space voor alle clients bijhouden, omdat niet allen de server en client verschillende paden doorlopen, maar ook alle clients dat doen. Dat kost erg veel geheugen. Als de server eerst een bevestiging stuurt, betekent dat dat hij de gestuurde operatie heeft getransformeerd, toegepast op zijn kopie van de wavelet en doorgestuurd. De client kan het pad dat de server doorloopt nu afleiden, en operaties sturen die op het pad van de server liggen. Doordat hierdoor het werk van de server vereenvoudigd wordt, en er minder bijgehouden hoeft te worden, hoeft de server maar één state-space voor alle clients samen bij te houden. Dit levert dus grote geheugenbesparingen op. Door operaties die ondertussen bij de client plaatsvinden samen te voegen, kan de client wel alles versturen, ook als er sneller operaties bijkomen dan dat er een bevestiging terugkomt.

Conflicten

Er kan een conflict ontstaan als twee mensen op dezelfde positie verschillende tekst invoeren. Er moet een afspraak zijn wie er dan als eerste aan de beurt is. Deze afspraak is vrij willekeurig: dat de gebruiker met grootste id voorgaat. Als er twee mensen op dezelfde positie iets verwijderen is er geen probleem. Als beiden hetzelfde proberen te verwijderen moet dat immers gewoon verwijderd worden.

Compositie

Operaties kunnen worden samengevoegd tot één nieuwe operatie. De mutaties van meerdere operaties kunnen in één lijst worden gezet, maar er gebeurd meer. Als er tekst ingevoegd wordt die direct na elkaar komt kan dat worden samengevoegd tot één mutatie die die hele tekst invoegt. Hetzelfde geldt voor verwijderen. Toevoegen en verwijderen kunnen tegen elkaar wegvallen.

Klassediagram

Een begin van de OT-implementatie is weergegeven in het volgende klassediagram:

OT-class-diagram.png

Operation

In deze module staat de klasse Operation centraal. Operaties kunnen worden samengevoegd, getransformeerd en toegepast op een wavelet. Een operatie houdt een lijst van mutaties bij aangezien een operatie daaruit is opgebouwd. Aangezien documentoperaties en participantoperaties qua implementatie niet van elkaar verschillen, maar er alleen verschil is in de mutaties zijn er geen aparte klasses voor document- en participantoperaties.

Mutation

Mutaties worden gerepresenteerd door de klasse Mutation. Ze hebben een inverse, die opgevraagd wordt met invert. Verder kan een mutatie toegepast worden op een wavelet met apply en samengevoegd met merge. Er zijn verschillende soorten mutaties die dit op verschillende manieren implementeren, dus Mutation is een pure abstract-klasse.

DocumentMutation

Er zijn verschillende documentmutaties, dus de klasse DocumentMutation is, net als Mutation abstract. Er zijn wel een aantal methodes die al geïmplementeerd kunnen worden. Alle soorten documentmutaties hebben een positie, die opgevraagd kan worden met getPosition en die gewijzigd kan worden met applyPositionChange. Ook kan de lengteverandering al berekend worden met de abstracte methoden getPreLength en getPostLength. Deze lengteverandering is nodig om te bepalen hoe de positie van de mutaties na deze mutatie veranderd moet worden.

ParticipantOperation

Ook van ParticipantMutations bestaan verschillende soorten, dus is ook deze klasse abstract. Aangezien ParticipantMutations altijd over een bepaalde deelnemer gaan, is er een veld participantId met bijbehorende get- en setmethodes om bij te houden over welke deelnemer het gaat.

Hoe verder vanaf hier

Milestone 1

  • Client en server kunnen Waves uitwisselen
  • Server kan al Waves kopiëren
  • Client kan Waves weergeven

Milestone 2

  • De client heeft een interface voor Waves veranderen
  • De server kan die veranderingen verwerken en opslaan, en verder verspreiden

Milestone 3

  • Server en client kunnen al samen een nieuwe Wave aanmaken
  • De server kan host-provider zijn voor zulke waves

We willen proberen milestone 1 zo snel mogelijk te bereiken in fase 1. Daarbij hebben we nog geen planning gemaakt, maar die willen we gaan maken aan het begin van die fase. Pas zodra we milestone 1 naar tevredenheid klaar hebben, gaan zowel wij als het clientgroepje gezamelijk over naar milestone 2.

Bronnen

Operational Transformation

Wave-protocol

XMPP en PubSub