Tk:Tutorial 5

From Carls wiki

Jump to: navigation, search

Sista delen, det stora avslöjandet om hur tk/Cocoon, med sina pipelines, lyckas uppdatera databasen. Istället för att sitta och trycka på hemligheten tar vi den direkt:

      (update.xmap | 99..101)
      <map:transform type="xmldb">
        <map:parameter name="base" value="{global:xmldb-base}/{host-config:host}/"/>
      </map:transform>

Här sker alltså uppdateringen. Ännu en transformer — det kunde man ju just tänka sig.

Självklart är denna uppdatering/transformer en del av en större kedja av händelser:

   (update.xmap | 81..120)
   <map:resource name="do-request">
     <map:aggregate element="root">
       <map:part src="cocoon://{doc-id}.xml"/>
       <map:part src="cocoon://request"/>
       <map:part src="cocoon://forms/{request-param:@type}.xml#//tk:template"/>
     </map:aggregate>
     
     <map:act type="ibg-upload">
       <map:parameter name="upload_dir" value="/var/tk/upload/{host-config:host}"/>
       <map:parameter name="obj_id" value="{request-param:@id}"/>
       
       <map:transform src="logic/xupdate/{request-param:@action}.xsl">
         <map:parameter name="docdir" value="{../doc-dir}"/>
         <map:parameter name="docid" value="{../doc-id}"/>
         <map:parameter name="uploadname" value="{UPLOADNAME}"/>
       </map:transform>
     </map:act>
     
     <map:transform type="xmldb">
       <map:parameter name="base" value="{global:xmldb-base}/{host-config:host}/"/>
     </map:transform>
     
     <map:transform type="ibg-objstore">
       <map:parameter name="host" value="{host-config:host}"/>
       <map:parameter name="upload_dir" value="/var/tk/upload/{host-config:host}"/>
     </map:transform>
     
     <map:transform src="logic/xu-result2html.xsl"/>
     <map:serialize type="html"/>
   </map:resource>
 </map:resources>

I ordning:

  1. Data aggregeras ihop från dokumentet som redigeras (cocoon://{doc-id}.xml), info om förfrågan (cocoon://request), samt vilka egenskaper det redigerade objektets typ har (cocoon://forms/{request-param:@type}.xml#//tk:template). (Varje gång det står cocoon:// så handlar det om en intern pipeline, och alla de finns deklarerade i sitemap.xmap. Tänk på dem som metodanrop till privata metoder.)
  2. En hemmagjord action ibg-upload (som finns att beskåda i WEB-INF/classes/se/uu/ibg/action/ObjectUpload.java) laddar upp eventuella attachments som man har skickat med i formuläret. Bilder och filer och sånt.
  3. Nu görs själva databasanropet.
  4. För att förstå vad ibg-objstore ska vara bra för, tänk dig att man har laddat upp en massa bilder på en sida, men sedan tar bort bildobjekten. Kvar ligger då de uppladdade bildfilerna utan att någon på sidan använder dem. (Du minns förstås kalabaliken kring upload-katalogen i våras…) ibg-objstore, skriven av Andreas, ser till att filer försvinner när de inte används längre på hemsidan.
  5. Resultatet transformeras till läsbar (X)HTML... (hm, och "resultatet" här är något ganska oansenligt: nån form av ok- eller error-svar från databasanropet. det är alltså inte själva objtet som lagrades eller nåt sånt)
  6. ...och serialiseras.

Och det är allt. Det finns lite mer i update.xmap som dirigerar in uppdateringen rätt, beroende på om det är en uppdatering, ett nytt objekt eller en borttagning — men det är inget svårt, och jag har inget särskilt att säga om det.

Istället tänkte jag ägna det sista lilla åt att komma tillbaka till de databasläsningar som görs i de tre första sitemapparna (html.xmap, protected.xmap, edit.xmap). Jag har skippat över de delarna, dels för att det skulle ha tagit fokus lite från viktigare detaljer, och dels för att det här med databaser aldrig var mitt specialområde. Jag var CSS- och Javascriptkillen i gruppen som utvecklade tk.

Men helt klart vill man ha kännedom om hur databasen fungerar. (Eller särskilt hur den inte fungerar, de stunder när den inte gör det.) (Eller vara kompis med någon som har det.)

Sent under 2003 (innan tk lanserades) höll jag på och kämpade med databasen, som då var eXist. Av allt i mitt jobb var nog den mest nära att ge mig magsår: den var buggig och klarade inte ens av de testfrågor som jag skickade till den. (Detta var dåligt för nattsömnen eftersom det var tänkt att människor skulle börja använda/förlita sig på sidan, men om inte databasen fungerar så fungerar inte sidan, punkt.) Det var någonstans där som Andreas började på IBG. Han sade "glöm eXist, vi provar Xindice", så det gjorde vi. Det funkade bra.

Nu är vi tillbaka på eXist igen, enligt Andreas för att Xindice inte är så aktivt utvecklat längre, och för att eXist har hunnit ikapp. Mycket mer vet jag inte om det, men eXist har definitivt förbättrats sedan 2003. Inte alls lika mycket here be dragons över det längre. Så, nu har du lite bakgrund, och har fått en känsla för att stabila XML-databaser (åtminstone de vi har använt) inte är mycket äldre än tk själv.

Det är alltså därför det står exist i definitionen av transformern xmldb, den som gjorde databasuppdateringen:

     (sitemap.xmap | 62..67)
     <map:transformer name="xmldb" src="org.apache.cocoon.transformation.XMLDBTransformer">
       <driver>org.exist.xmldb.DatabaseImpl</driver>
       <base>xmldb:exist:///db/tk</base>
       <user>******</user>
       <password>*******</password>
     </map:transformer>

Det är inte bara vid uppdateringar som den används. Man vill ju hämta ut sidor när man ska servera dem också. Ta aggregeringen i html.xmap, till exempel:

      (html.xmap | 21..26)
      <map:aggregate element="root">
        <map:part src="cocoon://request"/>
        <map:part src="cocoon://timestamp"/>
        <map:part src="cocoon://internal_layouts.xml#//tk:object[@id='2004-05-23_131500_005']" element="layout"/>
        <map:part src="cocoon://{doc-id}.do-includes.xml"/>
      </map:aggregate>

Båda de två understa komponenterna anropar interna pipelines som i sin tur gör databasanrop. De två interna pipeline:arna är definierade i sitemap.xmap:

      (sitemap.xmap | 504..508)
      <map:match pattern="*.xml#**">
        <map:generate src="{global:xmldb-base}/{host-config:host}/{1}.xml#{2}"/>
        <map:transform src="logic/xpath_clean.xsl"/>
        <map:serialize type="xml"/>
      </map:match>
      (sitemap.xmap | 478..484)
      <map:match pattern="*.do-includes.xml">
        <map:generate src="{global:xmldb-base}/{host-config:host}/{1}.xml"/>
        <!-- Resolve page includes -->
        <map:transform src="logic/pageinclude.xsl"/>
        <map:transform type="cinclude"/>
        <map:serialize type="xml"/>
      </map:match>

Det är raderna med <map:generate/> som är intressanta. Den där första variabeln {global:xmldb-base} är definierad lite tidigare i sitemap.xmap:

     (sitemap.xmap | 343..346)
     <global-variables>
       <xmldb-driver>org.exist.xmldb.DatabaseImpl</xmldb-driver>
       <xmldb-base>xmldb:exist:///db/tk</xmldb-base>
     </global-variables>

Det är allt gott och väl, men vad är det som får xmldb:exist: att betyda "databasanrop"? Här tog mitt kunnande slut, så jag var tvungen att googla: tydligen kallas den där typen av prefix för pseudoprotokoll (dvs till skillnad från http:// och ftp:// så har de ingen vettig objektiv existens, men de är vettiga för en viss Cocoon-installation). Pseudoprotokollen finns definierade i en fil WEB-INF/cocoon.xconf:

    (WEB-INF/cocoon.xconf | 589..595)
    <!-- xmldb pseudo protocol -->
    <!-- component-instance class="org.apache.cocoon.components.source.impl.XMLDBSourceFactory" name="xmldb" -->
    <!-- Patched version of the XMLDB pseudo protocol that supports the TK namespace. -->
    <component-instance class="se.uu.ibg.source.XMLDBSourceFactory" name="xmldb">
      <driver class="org.exist.xmldb.DatabaseImpl" type="exist"
              user="*****" password="******"/>
    </component-instance>

...vilket förklarar hur det kan fungera som det gör. Det som står i WEB-INF/cocoon.xconf knyter pseudoprotokollet xmldb:exist: till klassen org.exist.xmldb.DatabaseImpl (troligen i WEB-INF/lib/exist.jar), som vet hur man gör databasanrop. Case closed.