Die Technik

Dieses Projekt ist nicht nur die Darstellung von "Content", wie redaktionelle Texte, Essays und Prosa heute neudeutsch genannt werden. Ein weiteres Ziel war die Beschäftigung mit Techniken wie XML unter Einbeziehung von Einzelaspekten wie XSLT, DOM1-3, XPath. Weiterhin diente es für mich als Testplattform für die DOM-Umsetzung in Webbrowsern. Der Zugewinn an Perl-, Python und JavaScript-Kenntnissen ist sicher auch nicht verkehrt.

Nach über 15 Jahren (und das ist eine Ewigkeit im Netz) war es Zeit für eine Neuimplementierung - jQuery kümmert sich nun um DOM. Django ersetzt den Scriptwust auf der Workstation und dem vormals unter Tomcat laufenden XSLT-Prozessor. Es bietet zudem ein Online-Redaktionssystem und spart Resourcen auf dem Server. Mezzanine liefert mit Bootstrap und Standard-Templates ein flexibles aber sauberes und zeitloses Layout. Pünktlich zur geplanten Umstellung hat ein Betriebssystem-Update die alte Lösung übrigens unbrauchbar gemacht...

Der Plan

Die Idee war, die XML-Quellen möglichst unabhängig von späteren Strukturen speichern zu können und die Bezüge zwischen den einzelnen Texten automatisch auflösen zu können. Es werden dabei drei Ebenen unterschieden: die Speicherung der Texte im Dateisystem, die Verbindungen der Einzeltexte untereinander und die spätere Struktur auf dem Webserver.

Die lokale Speicherung erfolgte in einem Verzeichnis pro Kapitel, in das auch die Leser-Kommentare abgelegt werden. Die Struktur, in der diese Kapitel eingebunden ist, wird über eine getrennte XML-Datei festgelegt: zum Zwischenschieben oder Entfernen von Kapiteln ist es so nicht notwendig, die vorherigen oder nachfolgenden Artikel zu ändern. Es werden lediglich Einträge in der Struktur-Dateien hinzugefügt oder entfernt.

Auch wenn die Struktur, wie sie sich dem Betrachter zeigt, baumförmig ist, wurde die Artikel auf dem Server in einer flachen Hierarchie (keine Unterverzeichnisse) abgelegt. Dies vereinfacht vor allem den Upload geänderter Texte und die Umstellung auf andere Technologien. (Die erzeugten Namen werden durchaus aus der Baumstruktur der Texte generiert, dies aber nur aus Faulh^w^wzur Vereinfachung der Skripten und um "lesbare" URIs zu erhalten) Weiterhin gibt es keine Extensions (.html, .shtml, .xml, etc), was die Änderung von HTML auf SHTML und von dort auf die Ausgabe eines Servlets sehr vereinfacht hat. Lesenswert zu dem Thema ist übrigens "Cool URIs don't change" von Tim Berners-Lee.

Die Neuimplementierung behält einige der Abstraktionen bei, auch wenn alles inzwischen in einer Datenbank gespeichert wird. Die Artikel liegen weiterhin in einem DocBook-ähnlichen XML-Format vor, allerdings pro Artikel, nicht mehr als Einzelkapitel. Eine Änderung des XML triggert die Generierung der einzelnen Kapitel, deren Absätze dann als fertige HTML-Fragmente in einer eigenen Struktur abgelegt werden. Dies erlaubt die Beibehaltung der alten Struktur, Erzeugung der "Digest"-Versionen (alle Kapitel auf einer Seite), und dynamische Einbindung von Kommentaren, ohne das ursprüngliche XML bei jedem Zugriff neu zu parsen. Dazu lassen sich Kommentare, Begriffsdefinitionen und Links im Redaktionssystem bequem bearbeiten. Aber weiter mit der Historie.

Die Umsetzung

Um aus dieser losen Sammlung von XML-Dokumenten die endgültige Darstellung zu bauen, wurden mehrere Scripte benötigt. Zunächst (merge.py) werden die einzelnen Kapitel mit ihren Kommentaren eingelesen, Titel und Kurztitel extrahiert und zusammen mit der Strukturdatei das Inhaltsverzeichnis aufgebaut sowie Referenzen auf das vorherige, nächste und übergeordnete Kapitel eingetragen. In einem Zwischenschritt (makedirectory.py) werden noch fehlende Inhaltsverzeichnis-Seiten generiert. Nun werden die die Referenzen aufgelöst, externe Links, Autor-Hinweise und Stichworte erzeugt (finishxml.py). Mit einem weiteren Script (makedict.py) werden Wörterbuch-Einträge abgeglichen und in einzelne Dateien abgelegt. Die erzeugte XML-Dateien werden dann auf den Server kopiert, wo ein JavaServlet diese beim Aufruf in per XSLT in HTML umsetzt.

Das ganze ist nicht so komplex wie es klingt, aber eine Menge Tipparbeit: die (spärlich kommentierten) Python-Scripte sind insgesamt 1400 Zeilen lang. Insgesamt war die Python-Implementierung unter Verwendung von pyxml ca. 3 mal langsamer als die vorherige Perl-Implementierung mittels XML::LibXML. Kein Wunder: XML::LibXML ist eigentlich nicht viel mehr als ein Wrapper für libxml2 ein Großteil des Codes läuft dort als C-Kompilat. Pyxml dagegen ist komplett in Python implementiert -- und Python ist für Baumstrukturen wirklich nicht die optimale Sprache.

Dies ist auch der Grund, warum die Neuimplementierung weiterhin das XML nur bei einer Änderung einmal parsed und beim Zugriff die fertigen HTML-Fragmente einfach zusammensetzt. Zumal die Verzeichniserstellung per Datenbankabfrage der Metadaten so deutlich einfacher und schneller ist und so "online"-fähig wird.

Fehlschlag: Perl und XML::LibXML

Zurück zum alten System. Was hatte mich also zur Umstellung von Perl auf Python bewogen? Eigentlich wären die Perl-Bindings zu libxml2 optimal, ja wenn sowohl die Bindings wie auch libxml2 selbst nicht so schrecklich instabil gewesen wären -- mit jeder Versionsänderung an einer der beteiligten Komponenten libxml2, Perl und XML::LibXML sind zwar einige der zahlreichen alten Bugs gefixt, aber dafür haben sich gleich genausoviele neue Abgründe aufgetan. Insbesondere das neue UTF-8-Handling von Perl 5.8 schien sich mit dem XML-Modul zu beissen... Damit flog mir das Modul mit einem "unknown error due XInclude at .../LibXML.pm line 340" um die Ohren. Zwar immer noch besser als die kommentarlosen Abstürze und fehlerhaft eingelesener Dateien vorheriger Versionen, den Grund gefunden habe ich aber trotzdem nicht (<xi:xinclude> auf Testdateien, auch verschachtelt, funktioniert natürlich anstandslos.) Ob sich das inzwischen gebessert hat? Keine Ahnung, ich habe Perl seitdem nicht mehr angefasst.

Verworfen: Java und Xerces

Eine Alternative zu Python wäre Java gewesen, aber die DOM-Implementierung von Xerces unterstützte per Default nur DOM1 und DOM2, ich benötigte jedoch einige DOM3-Features, die dort nur mit Neubau der Library verfügbar war (unter einem anderen Namespace) und jede Menge Handarbeit benötigen (zumindest als ich mir das zuletzt angesehen habe). Abgesehen davon mag das Design der Java-Implementierung zwar sehr sauber sein, für eine brauchbare Anwendung ist das aber viel zu komplex. Ob da wohl die selben Leute hinter stecken, die damals Xlib entworfen haben?

Zur Ehrenrettung von Java: immerhin hat die Server-Seite 16 Jahre mit nur minimalen Anpassungen gehalten. Nur dass der XSLT-Prozessor inzwischen so strikt ist, dass ohne Namespaces und Validierung nichts mehr geht und ich massiv Arbeit in das Projekt hätte stecken müsste, damit das wieder funktioniert.

Die Lösung: Python und pyxml

Welch Wohltat dagegen die Python-Implementierung! (Fast) alles was ich brauche ist in pyxml vorhanden (bis auf XInclude, aber das war schnell nachgebaut), die Umsetzung von Perl auf Python konnte fast 1:1 geschehen und bestand fast ausschließlich aus Tipparbeit, das Resultat sieht auch noch eleganter aus. Und vor allem: keine bösen Überraschungen. Da verschmerze ich auch den Einbruch an Performance -- war ja zum Glück alles offline. Portierungsaufwand, inklusive Python-Grundlagen lernen: 5 MT. So gehört das!

Und inzwischen läuft alles serverseitig mit Python. Dank Djangos Redaktionssystem kein ewige Sucherei nach den vergessenen Begriffserklärungen, falschen Includes oder kaputten Links mehr. Und die URIs sind immer noch cool!