Sonntag, 29. März 2009

Tip: Maven 2, Unit-Test Ausführreihenfolge

Das Maven 2-Plugin "surefire" wird verwendet um die Unit-Tests zu starten. Wie ich festgestellt habe kann es auf verschiedenen Plattformen (Linux, MacOS, ...) zu verschiedenen Ausführreihenfolgen dieser Tests kommen. Um das zu vermeiden kann man ein JUnit "TestSuite" erzeugen und im maven nur noch "TestSuiten" ausführen lassen.

Ein JUnit-TestSuite kann z.B. so aussehen:
   1 package testproject.tests.dao;
2
3 import org.junit.runner.RunWith;
4 import org.junit.runners.Suite;
5
6 @RunWith(Suite.class)
7 @Suite.SuiteClasses({
8 UserTests.class,
9 FolderTests.class
10 })
11
12 public class DAOTestSuite {
13 // the class remains completely empty,
14 // being used only as a holder for the above annotations
15 }

Erst werden die Tests in der Klasse "UserTests" (Zeile 8) und anschließend die Tests in der Klasse "FolderTests" (Zeile 9) ausgeführt.

In der Maven 2-Konfiguration (pom.xml) kann man dann die Konfiguration für das Surefire-Plugin wie folgt anpassen (Zeile 13-17):
   1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
3
4 <!-- ... -->
5
6 <build>
7 <!-- ... -->
8 <plugins>
9 <plugin>
10 <groupId>org.apache.maven.plugins</groupId>
11 <artifactId>maven-surefire-plugin</artifactId>
12 <version>2.4.2</version>
13 <configuration>
14 <includes>
15 <include>**/DAOTestSuite.java</include>
16 </includes>
17 </configuration>
18 </plugin>
19 </plugins>
20 </build>

"Nested Set" mit Hibernate und Spring

Das Modell "Nested Set" kann verwendet werden um Baumstrukturen mithilfe von Mengen in einer relationalen Datenbank zu speichern.

Man denkt bei einem Baum direkt an Dinge wie z.B. das Dateisystem: Ein Verzeichnis kann mehrere Unterverzeichnisse und diverse Dateien beinhalten. Diese Parent/Child-Beziehung (von den Verzeichnissen) kann man am einfachsten in der relationalen Datenbank folgendermaßen abbilden:
Tabelle: FOLDER
+----+-------------+-----------+
| ID | NAME | PARENT_ID |
+----+-------------+-----------+
| 1 | Parent1 | NULL |
| 2 | Child1 | 1 |
| 3 | Child2 | 1 |
| 4 | Parent2 | NULL |
| 6 | Child2 | 4 |
| 7 | ChildChild1 | 6 |
| 8 | Child1New | 4 |
+----+-------------+-----------+

Die in einem Verzeichnis enthaltenen Dateien müssen natürlich mit einer Relations-Tabelle realisiert werden. z.B.:
Tabelle: FOLDER_FILES
+-----------+-------------+
| FOLDER_ID | FILE_ID |
+-----------+-------------+
| 1 | 2 |
| 2 | 3 |
+-----------+-------------+

Jetzt kommt der Kunde und möchte z.B. nur noch die Verzeichnisse sehen die Dateien beinhalten. Das hört sich erstmal sehr einfach an - nur haben wir das Problem, dass ein Verzeichnis keine Dateien beinhaltet aber in einem Unterverzeichnis Dateien zu finden sein könnten. Wir müssten also Teilmengen selektieren können. Genau zu diesem Zweck kann das "Nested Set"-Modell verwendet werden.

Ein einfacheres Modell ist "Materialzed path" (Pfad-Modell) - hier wird zu jedem Knoten (in diesem Beispiel Verzeichnis) der komplette Pfad in eine zusätzliche Spalte gespeichert:
Tabelle: FOLDER
+----+-------------+---------+-----------+
| ID | NAME | PATH | PARENT_ID |
+----+-------------+---------+-----------+
| 1 | Parent1 | .1. | NULL |
| 2 | Child1 | .1.2. | 1 |
| 3 | Child2 | .1.3. | 1 |
| 4 | Parent2 | .4. | NULL |
| 6 | Child2 | .4.6. | 4 |
| 7 | ChildChild1 | .4.6.7. | 6 |
| 8 | Child1New | .4.8. | 4 |
+----+-------------+---------+-----------+

Nun kann man einen kompletten Teilbaum mit folgender SQL-Abfrage erhalten:
SELECT * FROM FOLDER WHERE PATH LIKE '.4.%'
Hier selektiert man das Verzeichnis "Parent2" inklusive aller Kinder.

Dieses Pattern ist mit Hibernate relativ einfach zu realisieren. Einfach eine zusätzliche Property/Spalte "PATH" hinzufügen und einen Interceptor schreiben, der vor dem Schreiben in die RDBMS den "Path" pro Element ermittelt.

Leider gibt es beim Modell "Materialized path" einige Restriktionen - u.a. die Länge der PATH-Spalte restriktiert die Verzeichnisbaumtiefe, die Performance auf eine Text-Spalte mit LIKE ist nicht optimal u.v.m

Das führt uns zum Modell "Nested Set". In diesem Blogbeitrag wurde das Prinzip sehr einfach und verständlich erklärt - deshalb werde ich mich hierzu nicht weiter auslassen (DRY-Prinzip). Ich werde mich hier auf die Implementierung in Spring und Hibernate konzentrieren.

Das Resultat in der Datenbank sieht dann wie folgt aus:
Tabelle: FOLDER
+----+-------------+-----------+-----------+---------+----------+
| ID | NAME | PARENT_ID | NS_THREAD | NS_LEFT | NS_RIGHT |
+----+-------------+-----------+-----------+---------+----------+
| 1 | Parent1 | NULL | 1 | 1 | 6 |
| 2 | Child1 | 1 | 1 | 2 | 3 |
| 3 | Child2 | 1 | 1 | 4 | 5 |
| 4 | Parent2 | NULL | 4 | 1 | 8 |
| 6 | Child2 | 4 | 4 | 2 | 5 |
| 7 | ChildChild1 | 6 | 4 | 3 | 4 |
| 8 | Child1New | 4 | 4 | 6 | 7 |
+----+-------------+-----------+-----------+---------+----------+

Hier handelt es sich um eine spezielle "Nested set"-Implementierung ist. Es gibt neben den Spalten "NS_LEFT" und "NS_RIGHT" noch die Spalte "NS_THREAD".

Um hier einen Teilbaum zu erhalten genügt folgende SQL-Abfrage:
SELECT * FROM FOLDER WHERE NS_THREAD = 4 AND NS_LEFT BETWEEN 1 AND 8

Die Implementierung des Interceptors, des Modells sowie die Hibernate-Konfiguration könnt Ihr hier begutachten.

Was man also von einem "Einzeiler" aus Rails, CakePHP und anderen Frameworks kennt ist bei Hibernate wirklich ziemlich komplex zu implementieren. Hier stellt sich die Frage ob Hibernate das nicht etwas einfacher für den Benutzer (Programmierer) machen sollte.

Weitere Links zum Thema:

Freitag, 13. März 2009

"Freitag der 13." Server-Upgrade (dist-upgrade)

Ich habe es gewagt an einem "Freitag den 13." ein Update von einigen meiner Linux-VServer von Debian 4.0 ("Etch") auf Debian 5.0 ("Lenny") zu machen.

Zu meiner Überraschung lief das ohne größere Probleme. Hier ein paar Notizen was so aufgetreten ist:

Neues Layout der Konfiguration von Apache2
Unter /etc/apache2/ hat sich einiges getan. u.a. ist die Datei /etc/apache2/apache2.conf kürzer geworden und einiges aus dieser Datei ist in das Verzeichnis /etc/apache2/conf.d gewandert. Das finde ich wirklich besser als vorher.

klogd mit sysklogd machten im Linux-VServer Probleme
klogd wollte im VServer einfach nicht starten. Hierfür gibt es eine einfache Lösung: einfach stattdessen rsyslog verwenden. Man kann sogar in der Datei /etc/rsyslog.conf den Eintrag "imklog" auskommentieren, da ein Linux-VServer ja kein Kernel-Logging benötigt.

neues PHP5 memory_limit
die php.ini von PHP5 hat jetzt ein memory_limit von 128M

Trac 0.11
Trac hat mich etwas mehr Zeit gekostet, da sich die Konfiguraton gegenüber der Version 0.10 etwas geändert hat. Vorher habe ich Trac in Verbindung mit FastCGI verwendet. Bei der Version 0.11 habe ich mich für modpython entschieden. Natürlich musste ich noch das WebAdmin-Plugin aus meinem "EggCache" löschen, da dieses Plugin seit 0.11 Bestandteil von Trac geworden ist.
Die Trac "Datenbank" muss mit dem Programm trac-admin und dem Kommando "upgrade" auf den neuesten Stand gebracht werden. Man sollte auch noch das Kommando "wiki upgrade" verwenden um die Standard-Wiki-Seiten auf die neue Version zu heben.

Subversion
Bei Subversion lief bis auf ein älteres Repository alles ohne Probleme. Das besagte Repository hatte das "bdb"-Layout, verwendete also das Berkeley-DB Backend und kam noch aus der Migration von Debian 3.1 ("Sarge"). Jetzt gab es beim Zugriff über den Apache (mod_dav_svn) eine Fehlermeldung. Als ich ein Dump (via svnadmin) von diesem Repository machen wollte kam ein bdb-Versionskonflikt Version 4.4/Version 4.6 zutage.
Abhilfe schaffte hier ein "svnadmin recover". Hiernach konnte ich ein "svnadmin dump" von dem Repository machen. Dann habe ich ein neues Repository angelegt (natürlich wie empfohlen "fsfs") und habe den Dump eingefahren. Et Voilà... das Repository funktioniert jetzt wieder tadellos.

Der Rest
Die restlichen Programme wie munin, mercurial, apt-cacher usw. machten beim Upgrade keinerlei Probleme.

Jetzt habe ich "nur" noch 6 Linux-VServer mit Debian 4.0 ("Etch") die ich dann auch noch irgendwann mal migrieren sollte... Im November gibt es wieder einen "Freitag den 13." - bis dahin habe ich hoffentlich schon die Zeit gefunden und alle VServer brav migriert.

Samstag, 7. März 2009

Das Leben ist ungerecht

Meine "bessere Hälfte" hat ein G1 (Android Plattform) bekommen (beruflich).

Das G1 ist wirklich super einfach zu bedienen - für ein Linux-System ;)

Was mich an dieser Android-Plattform reizt:
  • offenes/freies Linux-System
  • Programme lassen sich einfach mit Java programmieren
  • kostenloses/freies SDK
  • viele tolle Programme


Es gibt für diese relativ neue Plattform schon viele schöne Programme: z.B. Chatclients, Barcode-Scanner, DynDns-Client ... und sogar einen SSH-Client! Was jetzt noch fehlt ist eine Shell - am liebsten die zsh.

Übrigens: das G1 (aktuelle Firmware 1.1) läuft wohl mit einem Linux-Kernel 2.6.25.

Weitere Links:

Mittwoch, 4. März 2009

Tip: Server-Konfigurationsdateien in einer verteilten Versionsverwaltung

Das Problem
Konfigurationsdateien auf einem Server (i.d.R "/etc") ändern sich häufiger - und man möchte eine Historie - wann, was, warum modifiziert wurde. Das Problem wird noch schlimmer wenn mehrere Personen einen Server konfigurieren. Person A sollte wissen wann, was und warum Person B z.B. die Webserverkonfiguration modifiziert hat.

Welches SCM
Zuerst dachte ich an Subversion, da ich dieses SCM schon von der Programmierung kannte.

Folgende Punkte sprechen aber dagegen:
  • Subversion erzeugt in jedem Verzeichnis in der "Working Copy" ein ".svn"-Verzeichnis
  • die Geschwindigkeit bei vielen Dateien ist bei Subversion "nicht der Renner"
  • das Subversion-Backend ("Dateisystem") wird schnell sehr groß
  • das Konzept der "Working Copy" (welche erst ausgecheckt werden muss) und dem zentralen Repository ist hier leider etwas umständlich

Lösung: eine verteilte Versionsverwaltung.

Für diesen Zweck habe ich mir folgende verteilte Versionsverwaltungen angesehen:


Entscheidung: Ich habe mich für Mercurial aus folgenden Gründen entschieden:
  • einfache Verwendung
  • gute Performance, Repository-Größe klein
  • integrierter Webserver


Wie wird es gemacht
Schritt 1: man initialisiert ein neues Repository im "Root" des Dateisystems ("cd /"). Das geschieht mit dem Kommando "hg init". Nun legt Mercurial ein .hg-Verzeichnis in / an (es wird aber wirklich nur ein einziges Verzeichnis angelegt!).

Schritt 2: Dateien aus "/etc" zum Repository hinzufügen (unter Versionskontrolle stellen) geht mit dem Kommando "hg add /etc".

Schritt 3: Mit einem "hg commit" werden die neu hinzugefügten Dateien ins Repository übertragen.

Hier noch ein paar nützliche Kommandos:
Status abfragen: "hg status -amrd" (added, modified, removed, deleted)

Dateien umbenennen: mit "hg rename QUELLE ZIEL" kann man dann Dateien umbenennen. Man sollte also anstatt "mv" in Zukunft einfach dieses Kommando verwenden.

Dateien löschen: mit "hg remove DATEI/VERZEICHNIS" kann man die Datei oder das Verzeichnis aus der Versionierung nehmen. Ohne Parameter löscht "hg remove" auch die lokale Datei oder das Verzeichnis. Wenn man nur die Datei oder das Verzeichnis aus der Versionskontrolle nehmen will diese aber lokal behalten möchte sollte man "hg remove -Af DATEI/VERZEICHNIS" verwenden.

Unterschiede ansehen: mit "hg diff DATEI/VERZEICHNIS" kann man sich den Unterschied zwischen der lokalen Version und der im Repository befindlichen Version ansehen.

Repository für andere zugänglich machen: den eingebauten kleinen Webserver kann man mit "hg serve" starten. Dann kann man über "http://IP:8000" auf das Repository zugreifen. Hier ein paar Screenshots:







Repository "klonen": mit dem Kommando "hg clone http://URL ZIEL" kann man eine Repository-Kopie erstellen.

Grafischer Repository-Browser: sogar ein grafischer Repository-Browser ist bei Mercurial enthalten. Diesen bekommt man mit dem Kommando "hg view":



Konfiguration: Es gibt eine globale Konfiguration in "/etc/mercurial/hgrc" und pro User eine im Home-Verzeichnis "~/.hgrc". Mehr Informationen hierfür gibts mit "man hgrc".

Shell completion: Mercurial liefert wohl auch eine zsh-Completion mit. Dazu evtl. in einem folgenden Blog-Beitrag mehr.

Debian: Wer wie ich immer noch Debian Etch verwendet findet eine aktuellere Version von Mercurial im Backports-Reporitory.

Übrigens: das Projekt FSVS ist mir auch in den Sinn gekommen. Ich fand die Bedienung zu kompliziert und das Programm verwendet das SVN-Backend, was meiner Meinung nicht die beste Wahl hierfür ist. Dieses Programm berücksichtigt dann noch die Dateirechte und Benutzer/Gruppe. Das kann die hier vorgestellte Lösung nicht.

Update 14.03.2009: Wie ich heute gesehen habe gibt es auch das Projekt etckeeper das man mit mercurial, git, darcs und bzr verwenden kann. Es bietet Paketmanager-Integration und speichert Metadaten (u.a. Rechte) der Dateien mit ab. Sehr interessante Sache - das werde ich mal versuchen.

Weitere Links:

Dienstag, 3. März 2009

Tip: FritzBox-Verbindungsstatus mit Nagios überwachen

Ich habe mal mein nagios2-UPNP-Skript zur Überwachung der FritzBox-Verbindung hier publiziert. Der Quellcode ist nicht der Beste - für diese Aufgabe reicht er aber :)

Hier noch ein kleiner Screenshot aus nagios als "Beweis":


Wie man sieht ist das UPNP-Protokoll gar nicht schlecht... es muss nicht immer SNMP sein.

Wer weitere Infos zum Thema FritzBox sucht sollte mal hier vorbeischauen.

Montag, 2. März 2009

Tip: Server-Überwachung mit Visualisierung

Ich verwende seit ca. 2 Jahren zusätzlich zur Nagios-Überwachung meiner Server auch munin. Munin zeichnet kontinuierlich Daten auf und erzeugt schöne Grafiken daraus. So kann man z.B. feststellen, wann die CPU am wärmsten war oder wie stark die Datenmenge auf der Festplatte über einen Zeitraum zunimmt.

Hier ein paar Beispiele:




Hier kann man sehen, dass meine Daten auf der Festplatte innerhalb eines Jahres (02/2008 bis 02/2009) ziemlich zugelegt haben. Gut zu wissen, dass diese Steilkurve die Backup-Partition mit diversen rsnapshot-Ständen ist :)


...und hier wie die Temperatur zwischen Juni und August "ein Hoch" hat. Auch hier geht der Graph von 02/2008 bis 02/2009.

Die Installation von munin ist sehr einfach. Man installiert einfach auf den Server das Paket munin und für die zu überwachenden Knoten das munin-node Paket. Dann muss man dem Server nur noch die Knoten in der Datei "/etc/munin/munin.conf" mitgeben und fertig. Auf den munin-nodes kann man dann die einzelnen Plugins konfigureren. Tip für Debian-Benutzer: es können noch zusätzliche Plugins mit dem Paket munin-plugins-extra installiert werden.