Opakování
Transformátor je jedním ze základních typů komponent, na kterých je Cocoon postaven. Na vstupu přijímá XML data ve formě proudu událostí SAX. XML data jsou nějak transformována a transformátor posílá na výstup opět proud událostí SAX, které dostává na vstup další komponenta v rouře. Transformátor patří k těm složitějším komponentám, i když složitost v kontextu Cocoonu nemusí odpovídat tomu, co si pod pojmem „složitý program“ představuje průměrný vývojář nebo návrhář. Pro úspěšné naprogramování transformátoru je potřeba alespoň něco vědět o XML, mít představu, jak se používá rozhraní SAX (nebo alespoň DOM) a něco málo tušit o životním cyklu komponent v systému Avalon, na kterém je Cocoon založen. Nicméně naprogramovat primitivní transformátorek není zase tak obtížné.
Zadání
Naprogramujeme transformátor, který bude transformovat prvek <kokos>
z jmenného prostoru „ http://psykora.net/cocoon/trafo/1.0
“ na prvek <losos>, který bude obsahovat text „bla bla“ (plus případný další obsah prvku). Jiné elementy z výše uvedeného jmenného prostoru budou vypuštěny. Jinými slovy, budeme-li mít např. tato XML data na vstupu našeho transformátoru:
<a xmlns:t="http://psykora.net/cocoon/trafo/1.0"> <b> <t:kokos/> </b> <t:kokos>text</t:kokos> <t:cosi>aaa</t:cosi> </a>
měli bychom na jeho výstupu dostat toto:
<a> <b> <losos>bla bla</losos> </b> <losos>bla blatext</losos> aaa </a>
Realizace
Zdrojový kód transformátorů z Cocoonu lze najít v adresáři „ src/java/org/apache/cocoon/transformation
“. Abychom neměli problémy s překladem a spuštěním, přidáme do výše zmíněného adresáře také náš transformátor (samozřejmě v případě normálního projektu by bylo asi lepší mít vlastní strukturu adresářů oddělenou od Cocoonu). Náš transformátor bude implementovat třída MojeTrafo
package org.apache.cocoon.transformation; import org.apache.cocoon.ProcessingException; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import java.io.IOException; public class MojeTrafo extends AbstractSAXTransformer { public static final String MOJE_URI = "http://psykora.net/cocoon/trafo/1.0"; public static final String SOURCE_ELEMENT = "kokos"; public static final String TRANSFORMED_ELEMENT = "losos"; public MojeTrafo() { this.namespaceURI = MOJE_URI; } public void startTransformingElement( String uri, String name, String raw, Attributes attr) throws ProcessingException, IOException, SAXException { if (name.equals(SOURCE_ELEMENT)) { sendStartElementEvent(TRANSFORMED_ELEMENT); sendTextEvent("bla bla"); } } public void endTransformingElement( String uri, String name, String raw) throws ProcessingException, IOException, SAXException { if (name.equals(SOURCE_ELEMENT)) { sendEndElementEvent(TRANSFORMED_ELEMENT); } } }
MojeTrafo
- Určit jmenný prostor pro naše zdrojové prvky (např. v konstruktoru)
- Implementovat metodu
startTransformingElement
, která popíše reakci transformátoru na SAX událost začátku prvku z určeného jmenného prostoru (např. můžeme vygenerovat událost začátek transformovaného prvku) - Implementovat metodu
endTransformingElement
, která popíše reakci transformátoru na SAX událost konce prvku z určeného jmenného prostoru (např. můžeme vygenerovat událost konec transformovaného prvku).
„Logika“ našeho transformátoru je triviální, v normálních případech bude potřeba dostat a zpracovat další informace (konfigurace, HTTP požadavek, parametry, logování …). Třída AbstractSAXTransformer
toho pro nás „předžvýká“ poměrně dost, nicméně bude obvykle nutné implementovat i další metody. Rozhodně doporučuji podívat se na zdrojový kód nějakého jiného transformátoru, který je potomkem třídy AbstractSAXTransformer
(např. CIncludeTransformer
) i na kód třídy samotné.
Použití
Po překladu (resp. v našem případě je potřeba „rebuildovat“ Cocoon a restarovat jej, pokud běžel) můžeme vytvořit webovou „aplikaci“ s názvem „trafo“ např. s touto mapou:
<?xml version="1.0"?> <map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0"> <map:components> <map:transformers default="xslt"> <map:transformer name="trafo" src="org.apache.cocoon.transformation.MojeTrafo"/> </map:transformers> </map:components> <map:pipelines> <map:pipeline> <map:match pattern=""> <map:generate src="pokus.xml"/> <map:transform type="trafo"/> <map:serialize type="xml"/> </map:match> </map:pipeline> </map:pipelines> </map:sitemap>
Všechny potřebné soubory najdete v archivu, takže zbývá jen rozbalit, rebuildovat Cocoon a spustit aplikaci ( http://localhost:8888/trafo/
).
Možné problémy
Při tvorbě transformátorů je potřeba vzít v úvahu následující potenciální problém: Pokud dojde k chybě uvnitř transformátoru, která vyvolá výjimku, může lehce dojít k vytvoření dat, která nesplňují ani základní pravidla XML (tj. nejsou „well-formed“). Další komponenty v rouře se pak s takovými daty nemusejí vypořádat. Při ošetřování výjimek je vždy dobré se zamyslet, zda generovaná XML data jsou i v těchto případech v pořádku.
Závěr
Rád bych poděkoval laskavým čtenářům, kteří se prokousali seriálem až sem. Pokud jste měli někdy pocit, že Cocoon je složitý a že byste v něm nejraději nic nedělali, není to chyba Cocoonu, ale moje, že jsem to nedokázal dostatečně přístupně popsat. Máte-li pocit, že Cocoonu ještě něco chybí, aby vám stálo za to jej zvážit jeho použití pro vaši webovou aplikaci, podívejte se na něj podrobněji. Tento seriál nepopsal zdaleka vše, co je v Cocoonu obsaženo a co je v něm možné udělat.