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.