XML databáze navíc poskytují snadnou možnost změny zdrojových XML dat, protože podobně jako relační databáze obsahují standardní prostředky nejen na efektivní získávání dat, ale i na jejich modifikaci.
Nativní XML databáze
Cocoon verze 2.1.3 obsahuje pseudoprotokol xmldb
pro přístup do XML databází poskytujících XML:DB API, nicméně žádná taková databáze v něm obsažena není. V polovině února však vyšla verze 2.1.4, která (mimo jiné) obsahuje vestavěnou databázi Xindice verze 1.1 RC1 a některé nové komponenty (např. transformátor XMLDB). Jinou možností by bylo použít externí (tj. nikoliv vestavěnou) databázi. Pro naše účely bude ale asi nejvhodnější, když si do instalace Cocoonu 2.1.3, kterou jsme používali v předchozích příkladech, sami vestavíme jinou open source nativní XML databázi: eXist verze 1.0 beta 1. Navíc si tak ukážeme obecný způsob, kterým je možné rozšiřovat funkcionalitu Cocoonu.
Rozšiřujeme možnosti Cocoonu
Cocoon lze poměrně snadno rozšiřovat tak, že nakopírujeme potřebné knihovny jar mezi ostatní, které u zkompilované verze Cocoonu najdeme v adresáři build/webbapp/WEB-INF/lib
. Dále bývá obvykle nutné upravit soubory cocoon.xconf
a (nebo) web.xml
v adresáři build/webapp/WEB-INF
. V souboru cocoon.xconf
je definováno kromě jiného mapování tříd na komponenty (někdy to lze udělat pouze v mapě aplikace), deklarace logicsheetů apod. Pro náš příklad musíme alespoň přidat driver pseudoprotokolu xmldb
pro databázi eXist (bude se používat takto: xmldb:exist://...
) včetně jeho mapování na konkrétní třídu a generátor XQuery. Databáze eXist obsahuje ale i další komponenty (logicsheet pro XSP,transformátor XMLDB aj.). Pro zjednodušení jsou všechny potřebné soubory zabaleny v archivu cocoon-2.1.3_exist-1.0b1.tgz (cca 2 MB). Archiv obsahuje potřebné knihovny jar, konfigurační soubory, klienta databáze pro administraci, soubory pro náš příklad, a také připraví adresář build/webapp/WEB-INF/data
, do kterého si databáze eXist bude ukládat kolekce indexovaných XML dat. Archiv je nutné rozbalit v základním adresáři Cocoonu (standardně ~/cocoon-2.1.3
). POZOR! Rozbalením archivu přepíšete původní soubory cocoon.xconf
a web.xml
(jen ty „zkompilované“). Pokud jste v nich ale udělali nějaké změny, nezapomeňte si je předtím uschovat.
Spuštění databáze a příprava dat
Databáze se nyní bude spouštět automaticky s Cocoonem. Prostě jen spustíte (nebo restartujete) Cocoon. Při jeho prvním startu s databází by se měla objevit i hlášení o tom, že se vytváří prostor pro XML data a indexy. Úroveň logování databáze eXist je nastavena na „INFO“, takže později při práci s příkladem lze například lehce sledovat dobu odezvy databáze.
Příklad najdete v adresáři build/webapp/psc
. V souboru psc.xml
je databáze obcí a jejich částí (přes dvanáct tisíc prvků <sidlo>
) s atributy PSČ a krajem, kam obec patří. Tento soubor byl získán ze souboru ulice.xml, který je veřejně přístupný na serveru adres Ministerstva vnitra. Soubor byl pak zkonvertován jednoduchým stylesheetem ulice2psc.xsl
. Soubor psc.xml
je nutné nejdříve uložit do databáze. Ta je nastavena tak, aby kromě Cocoonu spolupracovala i se svým klientem přes protokol XML-RPC. Máme-li tedy již spuštěnou databázi (resp. Cocoon s databází), můžeme spustit databázového klienta z hlavního adresáře Cocoonu ( standardně cocoon-2.1.3
):
java -jar start.jar client
Po odkliknutí přihlašovacího okna (bez hesla) je spuštěn klient (zatím jej uvidíte bez kolekce „ psc
“):
Protože XML databáze spravuje data v kolekcích (odpovídá to zhruba adresářům souborového systému), uložíme soubor psc.xml
do kolekce „ psc
“. Vytvoříme tedy kolekci s názvem „ psc
“ a do ní vložíme soubor psc.xml
. Můžeme použit GUI nebo příkazovou řádku klienta. Na ní potřebný dialog vypadá takto (vstup uživatele je zvýrazněn):
exist:/db> mkcol "psc"
created collection.
exist:/db> cd "psc"
exist:/db/psc> put "build/webapp/psc/psc.xml"
storing document psc.xml (1 of 1) ...done.
parsing 833038 bytes took 10820ms.
parsed 833038 bytes in 10820ms.
Nyní můžete klienta ukončit (CTRL+Q) nebo vyzkoušet databázi dotazem na záznam obce Slavkov. Stisknutím CTRL+F (nebo v menu Tools/Find) otevřete nové okno, do jehož části „XQuery“ zadejte (bez uvozovek) tento výraz XPath: „ //sidlo[.='SLAVKOV']
“. Po odeslání (Submit) by v části „Results“ mělo být zhruba (zanedbal jsem prvek <exist:match>) toto:
<sidlo kraj="MORAVSKOSLEZSKÝ" psc="747 57"> SLAVKOV </sidlo>
Nyní máme připraveno vše pro naši aplikaci.
Aplikace: databáze PSČ
Aplikace je velmi jednoduchá. Spusťte Cocoon a v prohlížeči zadejte (bez uvozovek) toto URL: „ http://localhost:8888/psc/
“. Nezapomeňte na ukončující lomítko. Zobrazí se jednoduchý formulář. Do formuláře se zadává libovolná část jména obce nebo její části (pouze z České republiky) a po odeslání (a vrácení výsledku) se pod tímtéž formulářem objeví seznam obcí (resp. jejich částí) se směrovacími čísly. Níže je uvede výsledek hledání řetězce „SLAVKOV“ (zadávejte bez uvozovek):
O formulář i jeho zpracování se stará soubor hledej-psc.xq
zapsaný v XML Query (XQuery):
xquery version "1.0"; declare namespace request= "http://exist-db.org/xquery/request"; declare namespace util= "http://exist-db.org/xquery/util"; <html> <head> <title>Hledání</title> </head> <body> <h3>Hledání PSČ</h3> <form action="hledej-psc"> <p>Zadejte část názvu sídla: <br /> <input type="text" name="sidlo" id="sidlo"/> </p> <p> <button type="submit">Odeslat</button> </p> </form> { let $sidlo:=request:request-parameter("sidlo","") return if (string-length($sidlo) = 0) then <p>Řetězec nebyl zadán!</p> else <div> <p><em>Výsledky hledání pro <strong>*{$sidlo}*:</strong> </em></p> <ul> { let $query := concat( "//sidlo[contains(.,'", $sidlo, "')]" ) for $s in util:eval($query) return <li>{$s}</li> } </ul> </div> } </body> </html>
XQuery je standardní dotazovací jazyk pro dotazy nad kolekcemi XML dat. Specifikace jeho verze 1.0 je rozšířením jazyka XPath verze 2.0. Pokud jste se s jazykem XQuery ještě nesetkali, dobrý tutoriál, jeho druhéa třetí pokračování najdete na serveru firmy Oracle. Omezení (a rozšíření) jeho implementace v databázi eXist najdete v její dokumentaci. Program funguje tak, že nejprve je zobrazen formulář (viz „běžný“ (X)HTML kód pod deklaracemi). Pak následuje „výkonná“ část XQuery, která buď zobrazí nápis „Řetězec nebyl zadán“, pokud parametr „sidlo“ nebyl zadán, nebo zkonstruuje XPath (například „ //sidlo[contains(.,'SLAVKOV')]
“), dotáže se jím do databáze a vloží vrácený výsledek (posloupnost elementů) do dat generovaných v rouře.
Protože výše uvedený kód vkládá do čistého XHTML jakési „cizí“ elementy <sidlo>
, je zapotřebí tyto prvky převést na požadované zobrazení pomocí primitivního stylesheetu hledej-psc.xsl
.
Nyní se podívejme se na mapu aplikace:
<map:sitemap
xmlns:map="http://apache.org/cocoon/sitemap/1.0">
<map:components>
<map:generators default="file">
<map:generator name="xquery"
src="org.exist.cocoon.XQueryGenerator"/>
</map:generators>
</map:components>
<map:pipelines>
<map:pipeline>
<map:match pattern="">
<map:redirect-to uri="hledej-psc"/>
</map:match>
<map:match pattern="hledej-psc">
<map:generate src="hledej-psc.xq"
type="xquery">
<map:parameter name="use-request-parameters"
value="true"/>
</map:generate>
<map:transform src="hledej-psc.xsl"/>
<map:serialize encoding="UTF-8" type="html"/>
</map:match>
<map:match pattern="db/**">
<map:match type="request-parameter"
pattern="xpath">
<map:generate
src="xmldb:exist:///db/{../1}#{1}"/>
<map:serialize type="xml"/>
</map:match>
<map:generate src="xmldb:exist:///db/{1}"/>
<map:serialize type="xml"/>
</map:match>
</map:pipeline>
</map:pipelines>
</map:sitemap>
Není zde asi nic, co by nás mohlo překvapit nebo s čím jsme se již dříve nesetkali. V úvodu je deklarován generátor XQuery, pokud v HTTP requestu požadujeme „hledej-psc“, vybere se zvýrazněná roura, která nejprve vygeneruje XHTML formulář, v případě vráceného výsledku s „cizími prvky“ <sidlo>
. Ty se v dalším kroku XHTMLizují (to je ale slovo!) pomocí XSL transformace. Výsledek je pak serializován jako HTML a odeslán klientovi.
Na konci mapy je ještě jedna roura (pod tou výše uvedenou), která ukazuje použití pseudoprotokolu xmldb
. Pokud v prohlížeči zadáte (bez uvozovek) URL například: „ http://localhost:8888/psc/db/ psc/psc.xml ?xpath=//sidlo[contains(.,'OPAV')]
“ (pozn. ed.: obě mezery do URL přidány násilím kvůli sazbě –Johanka), vrátí se přímo XML prvky z databáze. Jako hodnotu parametru xpath
můžete samozřejmě použít jakýkoliv korektní výraz XPath. Pokud v databázi vytvoříte další kolekce a (nebo) uložíte do ní další XML soubory, můžete v dotazech použít i je (nahraďte jimi zvýrazněnou část URL). Při pokusech doporučuji vždy omezit výsledek na nějakou rozumnou množinu, jinak můžete u prohlížeče čekat velmi dlouho, než se data zobrazí.
Další výhody XML databáze
XML databáze ve spolupráci s Cocoonem nabízejí mnohem více možností, než je možné v tomto článku nastínit. Nicméně bych se rád dotknul možnosti použít XML databázi jako centrální úložiště. Pseudoprotokol xmldb
lze použít (skoro) všude, kde specifikujeme soubor. To znamená, že všechny XML soubory pro File generátor, stejně jako XSLT stylesheety pro XSLT transformátor (ale například i mapy) mohou být uloženy v XML databázi. To dává nový rozměr možnostem, jak lze zdrojové XML soubory a data strukturovat a co s nimi lze všechno dělat. Databáze eXist navíc umí ukládat i binární data, což se hodí, pokud je např. v databázi uložena dokumentace včetně obrázků. Zlepší se tak i škálovatelnost, protože databáze může běžet i na jiném stroji. Možnosti modifikace uložených dat mohou také hrát nezanedbatelnou roli, pokud budeme váhat, zda data uložíme jako soubory v systému, nebo v databázi.
Závěr
Nakonec bych chtěl upozornit na to, že zde výše uvedený příklad je opravdu primitivní a vlastně dost „špatně“ navržen (minimálně z hlediska Cocoonu). Určitě jste postřehli, že jsem si moc nelámal hlavu s oddělením obsahu a prezentace. V tomto konkrétním případě se směrovacími čísly by použití pseudoprotokolu xmldb
s následnou transformací do (X)HTML (nebo jiného formátu) bylo asi lepším a „čistším“ řešením. Ale jak bych pak ukázal to XQuery? :-)
Také k mému „zabudování“ databáze eXist do zkompilovaného Cocoonu lze mít výhrady. Je to spíše „hack“ než metodický postup. Změny ve zdrojové části by však byly komplikovanější i pracnější a nelze je řešit prostým rozbalením archivu, což značně překračuje možnosti tohoto článku.
Cocoon a XML databáze jsou přirození spojenci. My jsme zde přidávali databázi eXist do Cocoonu, ale i naopak: plná instalace databáze eXisttaké obsahuje Cocoon (resp. jeho část). Na jejich stránkách se tak můžete podívat na další příklady s Cocoonem i bez něj.