Datenbankanbindung mit Cocoon

Andreas Popper


Inhaltsverzeichnis

Vorbemerkung
1 Vorarbeiten
Datenbank
JDBC für PostgreSQL-7.0.3
JDBC und Cocoon
XML und XSL für SQL
2 Das SQL-Modul in Aktion
Layout
Funktionsumfang
A Die Files im Source-Code
Test des SQL-Moduls
sql01.xml
sql01.xsl
sql.dtd
Beispielanwendung für das SQL-Modul
dbformular.xml
dbformular.xsl
phpxml.php
dbabfrage.xml
dbabfrage.xsl
dbabfrage2.xsl
sql.dtd
B Quellenverzeichnis

Vorbemerkung

Das Folgende ist Auszug aus einem Artikel, der vom Autor für die iX 06/2001 geschrieben worden ist.

Kapitel 1. Vorarbeiten

Eine funktionierende xml-Produktionsumgebung sollte zur Verfügung stehen. Als nächstes wird der Zugriff auf eine relationale Datenbank benötigt; die Beispielkonfiguration soll hier mit PostgreSQL erfolgen. Um Fehlerquellen möglichst zu vermeiden bzw. sie besser ortbar zu machen, finden Installation und Konfiguration Schritt für Schritt statt: Zugriff auf PostgreSQL über den Prompt, über JDBC und schließlich über das sql-Modul von Cocoon.

Datenbank

Voraussetzung: PostgreSQL ist installiert, möglichst in einer Version 7.x: Es sind dort so wesentliche Features wie z.B referentielle Integrität hinzugekommen. Das übliche Heimatverzeichnis von PostgreSQL ist /usr/local/pgsql.
Als erstes wird eine Database benötigt, die dem gleichen User gehört, der auch Apache, Tomcat und Cocoon betreibt. Für Testzwecke kann das ruhig root sein. Darin braucht es wenigstens eine Tabelle:


createdb xml
# psql
=> CREATE TABLE adressen (
name CHAR(30),
vorname CHAR(30),
PLZ INTEGER,
ORT CHAR(20)
);

INSERT INTO adressen VALUES (
'Mustermann',
'Renate',
12345,
'Oggersheim'
);

Testen, ob das auch funktioniert hat:

# psql xml
=>SELECT * FROM ADRESSEN;

JDBC für PostgreSQL-7.0.3

Von der PostgreSQL-JDBC-Homepage den Treiber für PostgreSQL-7.0.3 herunterladen nach
z.B.:/usr/local/pgsql/jdbc/jdbc7.0-1.1.jar

Damit PostgreSQL auch auf TCP/IP Aufrufe hört - sowohl JDBC als auch das Tcl-Frontend PgAccess benutzen sie - muss in der Startdatei von PostgreSQL: /etc/rc.d/initd/postgres die Zeile einkommentiert werden:


# Add the "-i" option to enable TCP/IP sockets in addition
# to unix domain sockets.  This is needed for Java´s JDBC
#
PGOPTS="-i"

Danach wird PostgreSQL noch mitgeteilt, dass JDBC auch das Recht hat, sie zu bemühen. Entsprechend werden in: /usr/local/pgsql/data/pg_hba.conf die Zeilen eingetragen:

host         root   192.168.128.125   255.255.255.0     trust 
host	     geronimo 	192.168.128.125  255.255.255.0    trust
host	     xml 		192.168.128.125  255.255.255.0    trust 

Die neue Einstellung ist wirksam, nachdem man als User den Datenbankserver im Verzeichnis: /usr/local/pgsql/bin mit pg_ctl angehalten und mit postmaster -i wieder gestartet hat. Ab dem nächsten Boot stehen die Einstellungen dann automatisch zur Verfügung.

Die JDBC-Schnittstelle soll nun auf ihre Funktion getestet werden. Dazu wird für die oben angelegte Datenbank und Tabelle ein Java-Programm geschrieben: JDBC-Test für PostgreSQL, compiliert und ausgeführt mit: java Sample

Eine ausführliche Installationsanleitung für JDBC findet sich hier.

JDBC und Cocoon

JDBC muss für Cocoon erreichbar sein. Deshalb wird das jdbc7.0-1.1.jar-Archiv kopiert nach:
$TOMCAT_HOME/lib.
Als nächstes wird Cocoon für die Benutzung seines sql-Moduls konfiguriert, indem in
$TOMCAT_HOME/webapps/cocoon/WEB-INF/cocoon.properties sofern notwendig, die Zeile einkommentiert wird:


processor.type.sql = org.apache.cocoon.processor.sql.SQLProcessor

Anschließend Tomcat in $TOMCAT_HOME/bin mit ./shutdown.sh anhalten und mit ./startup.sh wieder starten, damit er das zusätzliche .jar-Archiv in seinen Classpath einbindet.
Jetzt kann die Funktion des Moduls getestet werden. Dazu habe ich einen kleinen .xml-File mit zugeörigem .xsl-Stylesheet erstellt entlang den Anweisungen in $COCOON_HOME/docs/sql.html, der offiziellen Cocoon-Doku.

XML und XSL für SQL

Der xml-File und sein Stylesheet werden nach $TOMCAT_HOME/webapps/cocoon gestellt und im Browser aufgerufen mit: http://localhost/cocoon/sql01.xml

Unsere oben angelegte Tabelle adressen wird, sofern alles in Ordnung ist und Apache und Tomcat laufen, vom .xml-File angesprochen: sql01.xml. Erst die Antwort der Datenbank wird an das dazugehörige .xsl-Stylesheet weitergeleitet sql01.xsl und entsprechend seinen Angaben nach HTML formatiert und vom Browser ausgegeben. Der Vollständigkeit halber sei auch die zugehörige DTD angegeben: sql.dtd.

Im Vergleich zur Anbindung und Ausgabe mit anderen Methoden, ob nun über ein Servlet, Perl oder PHP, ist diese Methode sehr viel komfortabler und weniger arbeitsaufwendig.

Kapitel 2. Das SQL-Modul in Aktion

Inhaltsverzeichnis

Layout
Funktionsumfang

Nach der Pflicht die Kür: Mit Cocoon und seinem sql-Modul soll ein interaktiver Datenbankzugang per Browser realisiert werden. Es handelt sich hierbei um eine Three-Tier-Architektur mit dem Browser als Frontend, Apache, Tomcat und Cocoon als Middleware und PostgreSQL als Backend. Anstehende Verarbeitungen erfolgen durch PHP4 .

Zunächst ist alles wie bei unserem Test: Die Datei: dbformular.xml nimmt die Angaben für das Formular auf. Das könnte an dieser Stelle auch in HTML geschehen; XML ist aber genau so geeignet, und es werden hier schon die Vorteile von XML genutzt.

Cocoon formatiert das Formular via dbformular.xsl und liefert es im .html-Format an den Browser aus. Die Eintragungen werden an phpxml.php geschickt, das sie als Variable entgegennimmt und die eigentliche .xml-Datenbankabfrage schreibt mit den Authentifizierungsdaten und der .sql-Anweisung: dbabfrage.xml .

Per Refresh wird dbabfrage.xml aktiviert, die Anfrage wird an die Datenbank weitergeleitet und die Antwort mit dem zugehörigen .xsl-Stylesheet dbabfrage.xsl formatiert und wiederum an den Browser ausgeliefert.

Layout

Das in dbabfrage.xsl verwendete <pre> hat zwar den Vorteil, dass es alles austut, was die Datenbankabfrage ergibt; aber optisch ansprechend ist es nicht gerade. Mit den Zeilennamen der zurückgelieferten Tabelle lässt sich eine beliebige Formatierung erstellen, z.B. dbabfrage2.xsl:


<xsl:template match="ROW">	
		<tr>
			<th><b>Name</b></th>	     			
     			<th><b>Vorname</b></th>
	 		<th><b>PLZ</b></th>
	 		<th><b>Ort</b></th>
	 	</tr>
	 	<tr>		
     			<td><xsl:value-of select="name"/></td>
	 		<td><xsl:value-of select="vorname"/></td>
	 		<td><xsl:value-of select="plz"/></td>
	 		<td><xsl:value-of select="ort"/></td>	
		</tr>			    
</xsl:template>

Funktionsumfang

Administratorrechte vorausgesetzt, stehen auf diese Weise über den Browser alle Funktionen zur Verfügung, die auch das Prompt-Frontend von PostgreSQL beinhaltet.
Darüber hinaus bietet das .sql-Modul über sein Authentifizierungstool alle Abstufungen in der Berechtigung für Datenbankzugriffe, die auch PostgreSQL bereitstellt.

Für dieses Beispiel wurde PHP benutzt, um mit wenig Aufwand die benötigte .xml-Datei zu erzeugen; es sind aber auch andere Wege denkbar. Beispielsweise könnte die Authentifizierung festgelegt und die .sql-Anfrage mit einem beliebigen .cgi-Script in eine eigene Datei geschrieben werden, die dann per DTD über eine externe Entitiy referenziert wird: sql.dtd. In dbabfrage.xml wird der File mit dem reinen sql-Code dann so eingebunden:


<query  connection="foo">
	&pg;
</query>

Darüber hinaus bietet Cocoon selbst mit dem .xsp-Modul ein sehr umfangreiches und mächtiges Tool für interaktive und dynamische Webseiten an.

Unser Beispiel ist nur eine Möglichkeit, wie das .sql-Modul von Cocoon genutzt werden kann. Mit dem oberen Teil von dbabfrage.xml haben wir einen Datenbankhandler formuliert, der im weiteren Verlauf des .xml-Files an beliebiger Stelle mit:


<query connection="foo"></query>

aufrufbar ist. Damit steht die Datenbank-Funktionalität jederzeit zur Verfügung. Darüber ließe sich z.B. die Verlinkung innerhalb der gesamten Site regeln oder Sammlungen von Web-Adressen pflegen, Kataloge oder Shopsysteme aufbauen und dergleichen. Das .sql-Modul von Cocoon verknüpft die Mächtigkeit von SQL mit der Funktionalität von XML.

Anhang A. Die Files im Source-Code

Hier noch einmal alle im Text erwähnten XML-XSL- und DTD-Files im Sourcecode, der Übersicht halber zusammengefasst.

Test des SQL-Moduls

Inhaltsverzeichnis

sql01.xml
sql01.xsl
sql.dtd

sql01.xml


<?xml version="1.0" encoding="ISO-8859-1" standalone="no" ?>
<?xml-stylesheet href="sql01.xsl" type="text/xsl"?>

<?cocoon-process type="sql"?>
<?cocoon-process type="xslt"?>

<!DOCTYPE page SYSTEM "sql.dtd">

<page>												<!-- root-element -->

  <connectiondefs>
    <connection name="foo">							<!-- Name DB-Verbindung -->
      <driver>org.postgresql.Driver</driver>
      <dburl>jdbc:postgresql://localhost/xml</dburl><!-- URL DB-VERbindung -->
    	<username>root</username>
		<password>fritz</password>
	</connection>	
  </connectiondefs>
  
  <tab>
  	<query connection="foo">						<!-- Aufruf DB-Verbindung -->
  		select * from adressen						<!-- DB-Abfrage -->
  	</query>
  </tab>
   
</page>
			
			

sql01.xsl


<?xml version="1.0" encoding="ISO-8859-1"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

<xsl:template match="page">

	<!-- Anweisung zur Ausgabe von html -->
	
   	<xsl:processing-instruction name="cocoon-format">
		type="text/html"
	</xsl:processing-instruction>
	
	<!-- html-Körper -->
	
   	<html>
    	<head>
     		<title>
     			PostgreSQL Search Results
     		</title>
    	</head>
    	<body bgcolor="#FFF8DC">
			<font face="helvetica">
    		<h1>PostgreSQL</h1>
			
				<!-- übernimmt Inhalte der .xml-Datei -->
				
     			<xsl:apply-templates/>
			</font>
   		 </body>
   </html>
</xsl:template>
  
  
<!-- wählt das selbstdeklarierten Tag 'tab' aus -->
  
<xsl:template match="tab"> 
 	<table border="2" cellspacing="0" cellpadding="6"
	 bordercolor='#000099' bgcolor="#CCCCFF">
		<tr>
			<th>Name</th>
			<th>Vorname</th>
			<th>PLZ</th>
			<th>Ort</th></tr>
			
				<!-- übernimmt Inhalte von 'tab' und damit
				das Ergebnis der Datenbankabfrage -->
				
				<xsl:apply-templates/>
	</table>
</xsl:template>


<!-- Formatiert das Ergebnis der Datenbankabfrage -->

<xsl:template match="ROW">	
	<tr>		
     	<td><xsl:value-of select="name"/></td>
	 	<td><xsl:value-of select="vorname"/></td>
	 	<td><xsl:value-of select="plz"/></td>
	 	<td><xsl:value-of select="ort"/></td>	 
	</tr>
</xsl:template>

</xsl:stylesheet>

			
			

sql.dtd


<!ELEMENT page (connectiondefs, tab)+>
	<!ELEMENT connectiondefs (connection)+>
	<!ELEMENT tab (query)+>
	<!ELEMENT connection (driver, dburl, username, password)+>
	
		<!ELEMENT driver (#PCDATA)>
		<!ELEMENT dburl (#PCDATA)>
		<!ELEMENT username (#PCDATA)>
		<!ELEMENT password (#PCDATA)>
		<!ATTLIST connection name (foo) #IMPLIED>
		
		
	
		<!ELEMENT query (#PCDATA)>
		<!ENTITY pg SYSTEM "http://localhost/cocoon/cocoon_sql/pg01.txt">
		<!ATTLIST query connection (foo) #IMPLIED>
		
			
			

Beispielanwendung für das SQL-Modul

dbformular.xml


<?xml version="1.0" encoding="ISO-8859-1" standalone="no" ?>
<?xml-stylesheet href="dbformular.xsl" type="text/xsl"?>
<?cocoon-process type="xslt"?>


<page>
	<form>
		<aut>
		<input name="db" type="text" size="30">
		Name der Datenbank: </input>
		<input name="user" type="text" size="30">Benutzername: </input>
		<input name="password" type="password" size="30">Passwort: </input>
		</aut>
		<inhalt>
		<textarea  name="sql"  type="text" rows="10" cols="30">
			SQL-Anweisung: 
		</textarea>
		</inhalt>		
	</form>
</page>

dbformular.xsl


<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/"> 	
   	<xsl:processing-instruction name="cocoon-format">
   		type="text/html"
   	</xsl:processing-instruction>
   	<html>
    	<head>
     		<title>
      			PostgreSQL Abfrage
     		</title>
    	</head>

	<body bgcolor="white">
	

	<!-- html Ausgabe -->

		<center>
			<p>   
				<br></br>
			</p>
			<p>
				<br></br>
			</p>
	
			<h1>php-test</h1><br></br>
			<p>
				Bitte geben Sie ihre sql-Anfrage in die Maske ein.
			</p>
			<xsl:apply-templates select="form"/>
			<xsl:apply-templates/>
		</center>
	</body>
	</html>
</xsl:template>

	
	
<xsl:template match="form">
	<table>
		<form action="http://127.0.0.1/php/uebung1/phpxml03.php" method="post">
			<xsl:apply-templates select="aut"/>
			<xsl:apply-templates select="inhalt"/>
		<tr>
			<td></td>
			<td>
			<p></p><p>
			<center>
			<input type="submit" value="php-Eintrag abschicken"/>
			</center>
			</p>
			</td>
		</tr>
		</form>
	</table>
	<input type="submit" value="php-Eintrag abschicken"/>
</xsl:template>

<xsl:template match="aut">		
		<xsl:for-each select="input">
		<tr>
			<td><xsl:value-of select="."/></td>
			<td>
				<input>
					<xsl:attribute name="name">
						<xsl:value-of select="@name"/>
					</xsl:attribute>
					<xsl:attribute name="TYPE">
						<xsl:value-of select="@type"/>	
					</xsl:attribute>
					<xsl:attribute name="SIZE">
						<xsl:value-of select="@size"/>
					</xsl:attribute>		
				</input>
			</td>
		</tr>
		</xsl:for-each>			
</xsl:template>

<xsl:template match="inhalt">	
	<xsl:for-each select="textarea">
		<tr>
			<td>
				<xsl:value-of select="."/>
			</td>
			<td>
				<textarea>
					<xsl:attribute name="name">
						<xsl:value-of select="@name"/>
					</xsl:attribute>
					<xsl:attribute name="TYPE">
						<xsl:value-of select="@type"/>	
					</xsl:attribute>
					<xsl:attribute name="rows">
						<xsl:value-of select="@rows"/>	
					</xsl:attribute>
					<xsl:attribute name="cols">
						<xsl:value-of select="@cols"/>
					</xsl:attribute>	
				</textarea>
			</td>
		</tr>
	</xsl:for-each>		
</xsl:template>

</xsl:stylesheet>

phpxml.php


<html>
<head>
  <meta http-equiv="refresh" content="1; 
  URL=http://localhost/cocoon/xml/uebung/php/dbabfrage.html">
  
  <title>php- eintrag aus xml</title></head>
<body>
<center>
<p>&nbsp;<p>
<h1>Verbindung zu PostgreSQL</h1>
<?php

	// Schaun, ob überhaupt was eingetragen wurde
	
	if(isset($sql)){
		print "<p><b>Ihre Anfrage wird bearbeitet.<br>
		 Bitte einen Moment Geduld</b></p>";
		}
?>
</center>
		
<?php		
		// Hierhin wird die eigentliche Datenbankabfrage
		// mit dem sql-Modul geschrieben
		
		$datei = '/usr/local/jakarta/dist/tomcat/webapps/cocoon/xml/uebung/php/dbabfrage.xml'; 
		$fpw = fopen($datei,"w");	// öffnet und löscht vorm Schreiben
		$fpa = fopen($datei,"a");	// öffnet und hängt an beim Schreiben
		 
		 				
		
		// xml-File zur Datenbankabfrage vial Cocoons Sql-Modul
		// 1.) Übernahme der Authentifizierung
		
		$file1 = '<?xml version="1.0" encoding="ISO-8859-1" standalone="no" ?>	
<?xml-stylesheet href="dbabfrage.xsl" type="text/xsl"?>
<?cocoon-process type="sql"?>
<?cocoon-process type="xslt"?>
	<page>												
 		 <connectiondefs>
    		<connection name="foo">							
      		<driver>org.postgresql.Driver</driver>
      		<dburl>jdbc:postgresql://localhost/'.$db.'</dburl>
    			<username>'.$user.'</username>
				<password>'.$password.'</password>
			</connection>	
  		</connectiondefs> 
 		 <tab>
  			<query  connection="foo">
		';	
			
				
		// 2.) sql-Kommando; Slashes müssen explizit entfernt werden
				
		$file2 = stripSlashes($sql);	
	
	
		// 3.) restlicher Teil des xml-Files
		
		$file3 = '
			</query>
  		</tab>  
	</page>'
		;
		
		// Schreiben des .xml-Files
		
		fwrite($fpw,"");			// löscht alten Inhalt				
		fwrite($fpa,"$file1");
		fwrite($fpa,"$file2");
		fwrite($fpa,"$file3");
		fclose($fpa);
		fclose($fpw);			
?>
</body>
</html>

dbabfrage.xml


<?xml version="1.0" encoding="ISO-8859-1" standalone="no" ?>	
<?xml-stylesheet href="dbabfrage.xsl" type="text/xsl"?>
<?cocoon-process type="sql"?>
<?cocoon-process type="xslt"?>
	<page>												
 		 <connectiondefs>
    		<connection name="foo">							
      		<driver>org.postgresql.Driver</driver>
      		<dburl>jdbc:postgresql://localhost/php</dburl>
    			<username>root</username>
			<password>fritz</password>
			</connection>	
  		</connectiondefs> 
 		 <tab>
  			<query  connection="foo">
		SELECT * FROM demo
			</query>
  		</tab>  
	</page>

dbabfrage.xsl



<?xml version="1.0" encoding="ISO-8859-1"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

<xsl:template match="page">

	<!-- Anweisung zur Ausgabe von html -->
	
   	<xsl:processing-instruction name="cocoon-format">
		type="text/html"
	</xsl:processing-instruction>
	
	<!-- html-Körper -->
	
   	<html>
    	<head>
     		<title>
     			PostgreSQL Search Results
     		</title>
    	</head>
    	<body bgcolor="#FFF8DC">
			<font face="helvetica">
    		<h1>PostgreSQL</h1>
			
				<!-- übernimmt Inhalte der .xml-Datei -->
				
     			<xsl:apply-templates/>
			</font>
   		 </body>
   </html>
</xsl:template>
  
  
<!-- wählt das selbstdeklarierten Tag 'tab' aus -->
  
<xsl:template match="tab"> 
 	
				
				<xsl:apply-templates/>
	
</xsl:template>


<!-- Formatiert das Ergebnis der Datenbankabfrage -->

<xsl:template match="ROW">	
	<pre>		
     	<xsl:apply-templates/>	 
     </pre>
</xsl:template>

</xsl:stylesheet>

dbabfrage2.xsl



<?xml version="1.0" encoding="ISO-8859-1"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

<xsl:template match="page">

	<!-- Anweisung zur Ausgabe von html -->
	
   	<xsl:processing-instruction name="cocoon-format">
		type="text/html"
	</xsl:processing-instruction>
	
	<!-- html-Körper -->
	
   	<html>
    	<head>
     		<title>
     			PostgreSQL Search Results
     		</title>
    	</head>
    	<body bgcolor="#FFF8DC">
			<font face="helvetica">
    		<h1>PostgreSQL</h1>
			
				<!-- übernimmt Inhalte der .xml-Datei -->
				
     			<xsl:apply-templates/>
			</font>
   		 </body>
   </html>
</xsl:template>
  
  
<!-- wählt das selbstdeklarierten Tag 'tab' aus -->
  
<xsl:template match="tab"> 
 	<table border="2" cellspacing="0" cellpadding="6"
	 bordercolor='#000099' bgcolor="#CCCCFF">
		<tr>
			<th>Name</th>
			<th>Vorname</th>
			<th>PLZ</th>
			<th>Ort</th></tr>
			
				<!-- übernimmt Inhalte von 'tab' und damit
				das Ergebnis der Datenbankabfrage -->
				
				<xsl:apply-templates/>
	</table>
</xsl:template>


<!-- Formatiert das Ergebnis der Datenbankabfrage -->

<xsl:template match="ROW">	
	<tr>		
     	<td><xsl:value-of select="name"/></td>
	 	<td><xsl:value-of select="vorname"/></td>
	 	<td><xsl:value-of select="plz"/></td>
	 	<td><xsl:value-of select="ort"/></td>	 
     </tr>
</xsl:template>

</xsl:stylesheet>


sql.dtd

sql.dtd

Anhang B. Quellenverzeichnis

Apache 2.0:
iX 02/2001
Apache download:
httpd.apache.org/dist
Cocoon-Homepage:
xml.apache.org/cocoon
Cocoon Entwicklerversion:
xml.apache.org/cocoon/cocoon2.html
Java and XML,
Brett McLaughlin, O'Reilly 2000, S. 234ff.
JDBC für PostgreSQL:
http://www.retep.org.uk/postgres
Linkliste zu XML etc.:
http://www.xmlsoftware.com/publishing
PostgreSQL-download:
ftp://ftp.de.postgresql.org/pub/v7.0.3
PostgreSQL-Entwicklerversion:
http://www.postgresql.org/devel-corner/docs/postgres/cvs.htm
PostgreSQL: Introduction and Concepts,
Bruce Momijan, Addison/Wesley 2001
SQL-Dokumentation:
http://www.itl.nist.gov/fipspubs/fip127-2.htm
Tomcat download:
jakarta.apache.org/builds/jakarta-tomcat/release/v3.2.1/src/
XML-Installs und -Skripte:
tecfa.unige.ch/guides/xml/pointers.html
XML Professionell,
Richard Anderson et al, mitp Verlag, Bonn 2000
XML-Recommendation:
http://www.w3.org/TR/1998/REC-xml-19980210
XML-Sites im Netz:
xml.apache.org/cocoon/hosting.html
XPATH-Recommendation:
http://www.w3.org/TR/xpath
XSLT-Tutorium:
http://www.nwalsh.com/docs/tutorials/xsl/xsl/frames.html
XSLT-Tutorium:
http://www.zvon.org/xxl/XSLTutorial/Books/Book1/index.html
XSLT-Anleitung:
http://www.ibiblio.org/xml/books/bible/updates/
XSLT-Recommendation:
http://www.w3.org/TR/xslt