« December 2007 | Main | February 2008 »

January 2008 Archives

January 8, 2008

Die Nebenläufigkeit

Ich sitze nun schon seit einiger Zeit an meinem Client und bin dabei, die Funktionalität soweit zu verwirklichen, dass ein einfaches Arbeiten (CRUD), mit jeweils User, Task und Group möglich ist. Ich habe bemerkt, dass die Oberfläche die wenigsten Probleme macht, was eine Herausforderung ist, sind die vielen nebenläufigen Request-Abfragen.

Ich habe (hatte) mich dazu entschlossen (hier mal anhand eines Users), jeweils per JS ein User Objekt (welches von den Attributen her meinem JPA Objekt entspricht), sowie ein UserService Objekt, welches den Zugriff (Ändern/Laden/Speichern/Cachen) meiner User-objekte.
Liegt ein Objekt nicht im lokalen Cache (ein Array aus User-objekten) vor, so soll es Remote vom Server geholt werden.
Das klappt auch soweit alles. Leider handelt es sich bei dem Request (AJAX-typisch) um einen asynchronen Request,so dass die Daten im Hintergrund geladen werden, während die Verarbeitung im Vordergrund weiter läuft. Es passiert dann des öfteren, dass auf Daten zugegriffen werden soll, die noch nicht vollständig geladen sind. Dies passiert zum Beispiel auch, wenn innerhalb eines Task-Objektes ein User-Objekt abgefragt wird, welches noch nicht lokal im Cache vorhanden ist. Nach einiger Literatur (u.a. auch auf den Developer Seite bei Yahoo) und ein wenig Austausch mit Willem (vielen Danke noch mal hierfür) bin ich nun zu dem Ergebnis gekommen, dass ich meine Architektur so abändere, sodass diese stärker mit Callbacks arbeiten.
YUI sieht hierbei ein Callback-Objekt vor, welches jeweils eine Methode bei Erfolg und eine Methode bei Misserfolg zur Verfügung stellt.
Ein großes Problem ist hierbei aber auch die Übergabe der Referenzen.

Da zum Beispiel meine UserService Klasse instanziert wird und das Cache Array dann ein Attribut des Objektes ist.
So ist es dann notwendig, dass einer Callback-Klasse diese Referenz ebenfalls zur Verfügung gestellt wird.
Dies kann man u.a. durch ein "Zwischen referenzieren des this-pointers machen:

COTODO.UserService.prototype.getRemoteAndCallback = function(id, callback) {
    function failureHandler(o){
        var out = "COTODO.UserService.getRemote: id="+id;
        //Referenz steht normalerweise im UserAttribute
        var rUrl = "resources/users/"+id+"/"; 
        var self = this;
        YAHOO.log("Failure handler called; http status: " + o.status, "warn", "User.js");
    }
    YAHOO.util.Connect.asyncRequest('GET', sUrl, { success:callback, failure:failureHandler });
};
Da self in diesem Fall im Bezug auf eine mögliche Callback-Methode innerhalber der Methode getRemoteAndCallback im globalen Kontext steht, ist es ohne weiteres möglich, auf die Methoden von self zu zugreifen.

Im Moment bin ich dabei so Dinge wie:

          //US = UserService Instanz

COTODO.TaskService.prototype.getCreatedBy = function(){
    return US.get(this.created_by);
}

Auf eine Callback-Variante um zubauen. Letztendlich sah meine get Methode bisher wie folgt aus:
COTODO.UserService.prototype.get = function(id){
        if(!this.exists(id)){
            return this.getRemote(id);
        }
        COTODO.debug(this.list[id],"get");
        return this.list[id];
    };
Da aber in getRemote ein nebenläufiger Request abgesetzt wird, muss ich das so umbauen, dass die Methodenaufrufe quasi von der "anderen Richtung" her ausgelöst werden, also nach Abschluss des Requestes erst aktiviert wird. Andernfalls kommt es des öfteren zu einem NULL Objekt. Meine erste - zugebenermaßen naive Idee - eine Art globales Wait-Flag im User-Objekt zu hinterlegen, um die nebenläufigen Threads zu synchronisieren, habe ich wieder begraben, weil so Dinge wie:
while(!US.get(this.created_by).done){}
Sich nur schwer bis gar nicht kontrollieren lassen. Und entweder den Status ändern (also auf TRUE gesetzt werden), obwohl das Objekt noch nicht geladen ist oder einfach in Endlosschleifen verenden.

Eine weitere interessante Möglichkeit wäre die Nutzung von XML-Inseln. Also die Möglichkeit, dass jedes Objekt (und in diesem Objekt dann jeweils eine Callback-Methode) dafür verantwortlich ist, dass seine eigene Daten in den DOM Tree der Seite gesetzt werden.
D.h. jedes User-Objekt würde sicher selber - bei Bedarf - als "li" - node in ein "ul" Element auf der Seite setzen und so auch tatsächlich in der GUI die Userliste  füllen. Alle User Attribute könnte man entweder als li Attribute setzen (den Tag in dieser Hinsicht "verschmutzen") oder aber halt in einen versteckten Layer packen. Sicherlich eine interessante Idee, aber letztendlich halt nicht wirklich 100% MVC, aber durchaus gängige Praxis im AJAX Umfeld.

Mein UserService  würde dann auch kein  internes Cache Array haben - sondern direkt auf  den DOM-Knoten zugreifen.

Um den Zugriff zu vereinfachen habe ich mir schon ein paar Ersatz-Methoden geschrieben, um die Dinge zu verkürzen. Wer Prototype kennt, wird sich zuhause fühlen.

Object.prototype.$V = function(id, num){
    var num = num || 0;
    try{
        return this.$E(id)[num].firstChild.nodeValue;
    }catch(error){
        return null;
    }
};

Object.prototype.$E = function(id){
    return this.getElementsByTagName(id);
};

Letztendlich werden hier aber noch alle JS-Objekte verändert, sodass auch ein Array besagte Methoden enthält.
Ich werde mal schauen ob sich das irgendwie noch verfeinern lässt.

Blogged with Flock

January 9, 2008

XSLT - Exkurs

Weil ichs mal in Erwägung gezogen habe, aber Abstand davon genommen habe, da es eine wirklich nützliche XSLT Lib für JavaScript nocht nicht gibt.
Das Problem ist, dass man XML Daten bekommt (so wie das bei mir der Fall ist), aber diese Daten zum Beispiel als HTML Inseln (siehe letzter Post) in seinen DOM Baum "einhängen" möchte.
Man könnte sich ja jetzt fragen, wieso man dann nicht gleich HTML als Ausgabeformat nutzen möchte, aber letztendlich soll es ja alles schön kompatibel zu anderen Client bleiben und ich denke mal, dass das von mir gewünschte Format alles andere als Standard wäre. Es fehlt also eine Anweisung, um XML in das gewünschte HTML Format zu überführen. Genau das bietet einem XSLT (Extensible Stylesheet Language Transformation).
Hierbei wird durch ein XSL-Script fest gelegt, welche Daten wo im Ausgabeformat (in meinem Fall also HTML) landen sollen.

Also nehmen wir folgendes XML File:

user.xml:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<user uri="http://localhost:8080/cotodo/resources/users/3/">
    <email>Test3</email>
    <forename>Test</forename>
    <surname>NeuerTester</surname>
    <userId>3</userId>
    <created uri="http://localhost:8080/cotodo/resources/users/3/tasks/">
        <taskRef uri="http://localhost:8080/cotodo/resources/users/3/tasks/1/">
            <taskId>1</taskId>
        </taskRef>
        <taskRef uri="http://localhost:8080/cotodo/resources/users/3/tasks/2/">
            <taskId>2</taskId>
        </taskRef>
    </created>
    <groups uri="http://localhost:8080/cotodo/resources/users/3/groups/">
        <groupRef uri="http://localhost:8080/cotodo/resources/users/3/groups/1/">
            <groupId>1</groupId>
        </groupRef>
        <groupRef uri="http://localhost:8080/cotodo/resources/users/3/groups/2/">
            <groupId>2</groupId>
        </groupRef>
    </groups>
    <inbox uri="http://localhost:8080/cotodo/resources/users/3/tasks/">
        <taskRef uri="http://localhost:8080/cotodo/resources/users/3/tasks/2/">
            <taskId>2</taskId>
        </taskRef>
    </inbox>
    <outbox uri="http://localhost:8080/cotodo/resources/users/3/tasks/">
        <taskRef uri="http://localhost:8080/cotodo/resources/users/3/tasks/2/">
            <taskId>2</taskId>
        </taskRef>       
    </outbox>
</user>

Wie man gut erkennt, eine - recht tief - verschachtelte Ansammlung von Datenblöcken. Wichtig für REST sind zum Beispiel auch die Referenzen auf die verschiedenen Objekte (URIs), um ggf. Objekte vollständig nach zuladen.

Meine gewünschte HTML Ausgabe soll dann so aussehen:

user.html:

<li class="user" id="3" uri="http://localhost:8080/cotodo/resources/users/3/">
    <ul class="attributes">
        <li id="email">Test3</li>
        <li id="forename">Test</li>
        <li id="surname">NeuerTester</li>
    </ul>
    <ul class="groups" style="display:none" uri="http://localhost:8080/cotodo/resources/users/3/groups/">
        <li class="groupRef">1</li>
        <li class="groupRef">2</li>
    </ul>
    <ul class="outbox" style="display:none" uri="http://localhost:8080/cotodo/resources/users/3/outbox/">
        <li class="taskRef">2</li>
    </ul>
    <ul class="inbox" style="display:none" uri="http://localhost:8080/cotodo/resources/users/3/inbox/">
        <li class="taskRef">2</li>
    </ul>
    <ul class="created" style="display:none" uri="http://localhost:8080/cotodo/resources/users/3/created/">
        <li class="taskRef">1</li>
        <li class="taskRef">2</li>
    </ul>
</li>

Es handelt sich letztendlich um ein Listenelement (welches dann in einer users Liste landet), welches weitere Unterlisten enthält. Die Anzahl der Referenzen habe ich - zur optimaleren Platzausnutzung und zur Übersichtlichkeit - verringert. Letztendlich reicht mir eine Referenz der Liste und kann dann mit Hilfe der RefIds auf die URIs der einzelnen Elemente zugreifen. Das display:none habe ich nur aus dem Grund eingefügt, dass gewisse Elemente nicht sichtbar sein müssen. Mir reicht, dass sie im DOM hinterlegt sind.

Um also so eine Ausgabe zu erreichen benutze ich folgendes Script:


user.xsl:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="html"/>

    <xsl:template match="/">
        <xsl:element name="li">
            <xsl:attribute name="class">user</xsl:attribute>
            <xsl:attribute name="id"><xsl:value-of select="user/userId" /></xsl:attribute>
            <xsl:attribute name="uri"><xsl:value-of select="user/@uri" /></xsl:attribute>
            <xsl:element name="ul">
                <xsl:attribute name="class">attributes</xsl:attribute>
                    <li id="email"><xsl:value-of select="user/email"/></li>
                    <li id="forename"><xsl:value-of select="user/forename"/></li>
                    <li id="surname"><xsl:value-of select="user/surname"/></li>
        </xsl:element>
        <xsl:element name="ul">
                    <xsl:attribute name="class">groups</xsl:attribute>
                    <xsl:attribute name="style">display:none</xsl:attribute>
                    <xsl:attribute name="uri"><xsl:value-of select="user/@uri" />groups/</xsl:attribute>
                    <xsl:for-each select="user/groups/groupRef">   
                        <li class="groupRef"><xsl:value-of select="groupId"/></li>
                    </xsl:for-each>
                </xsl:element>
                <xsl:element name="ul">
                    <xsl:attribute name="class">outbox</xsl:attribute>
                    <xsl:attribute name="style">display:none</xsl:attribute>
                    <xsl:attribute name="uri"><xsl:value-of select="user/@uri" />outbox/</xsl:attribute>
                    <xsl:for-each select="user/outbox/taskRef">
                        <li class="taskRef"><xsl:value-of select="taskId"/></li>
                    </xsl:for-each>
        </xsl:element>   
                <xsl:element name="ul">
                    <xsl:attribute name="class">inbox</xsl:attribute>
                    <xsl:attribute name="style">display:none</xsl:attribute>
                    <xsl:attribute name="uri"><xsl:value-of select="user/@uri" />inbox/</xsl:attribute>
                    <xsl:for-each select="user/inbox/taskRef">   
                        <li class="taskRef"><xsl:value-of select="taskId"/></li>
                    </xsl:for-each>
        </xsl:element>   
                <xsl:element name="ul">
                    <xsl:attribute name="class">created</xsl:attribute>
                    <xsl:attribute name="style">display:none</xsl:attribute>
                    <xsl:attribute name="uri"><xsl:value-of select="user/@uri" />created/</xsl:attribute>
                    <xsl:for-each select="user/created/taskRef">   
                        <li class="taskRef"><xsl:value-of select="taskId"/></li>
                    </xsl:for-each>
        </xsl:element>           
    </xsl:element>      
    </xsl:template>
</xsl:stylesheet>

Hierbei gelten folgende Regeln: Der Zugriff auf die einzelnen Elemente erfolgt über XPath. Also <xsl:value-of select="user/userId" /> bedeute, dass der Wert aus der Hirachie user/ geholte wird, welche zwischen den Tags userId steht. Möchte man auf ein Attribute zugreifen, so benutzt man das @ Zeichen. Wie z.B. bei <xsl:value-of select="user/@uri" />. Ich habe mich immer für die "Root" URI entschieden, weil die anderen URIs sich per Definitionem davon ableiten lassen.

wollte man das XML Document nun direkt als HTML ausgeben haben, so fügt man in obiges Dokument unter

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

folgendes ein:

<?xml-stylesheet type="text/xsl" href="user.xsl"?>


Hierbei sollte die Datei natürlich unter dem gleichen Pfad erreichbar sein wir die user.xml. (Aus Testzwecken war das bei mir der Fall). Aber natürlich kann man auch jede andere URL angeben. Das Einbinden entspricht ungefähr dem von einer CSS Datei.

Empfehlenswert zu dem Thema ist:

sarissa - eine JS Implementierung von XSLT
Die XSL Tutorials von TopXML
XML/XSL Tutorials von DrWeb
und zu guter Letzt:
Die XML/XSL Tutorials von den W3Schools


Blogged with Flock

January 14, 2008

JavaScript DOM Object Parser

Ich sitze grade mitten drin, meine XML Ausgaben zu zerlegen um an die Daten zu kommen.
Da das teilweise wirklich unübersichtlich wird, habe ich mir zweit Stunden Zeit genommen und mir einen Parser  geschrieben, der mir ein beliebiges DOM Objekt als Baum ausgibt.

Aus dieser XML Struktur:

baut er mir zum Beispiel das hier:

Um das Ganze noch mal näher zu betrachten:


Der Parser ansicht befindet sich in einer JS Datei namens Debug.js

Diese bindet man über

 <script type="text/javascript" src="debug.js"></script>

ein und mit

 DEBUG.moveDIV(600,25);

kann man das Ausgabefenster noch an eine Stelle verschieben, wo es nicht stört.
Das eigentlich parsen passiert dann so:

 DEBUG.showParse(object);

Hierbei kann Object z.B. document sein, als auch zum Beispiel o.responseXML.documentElement.

Das Script selber ist hier zu finden.

Blogged with Flock

January 19, 2008

1. GUI und die Sicherheit

Die letzte Zeit habe ich mehr mit Schreiben verbracht. Nachdem ein JavaScript-Unterbau soweit lauffähig ist, habe ich angefangen, meine GUI-Skizzen soweit wie möglich einzubauen:

Ich bin dazu übergangen, anstatt statischen HTML-Code als String zusammen zu bauen (was eh langsam ist), alle Inhalte aus DOM Objekte zusammen zu setzen. Das hat den großen Vorteil, dass ich Elemente auch im Nachhinein per JavaScript mit Hilfe von Callback-Funktionen verändern kann.
Bisher muss man den Benutzer noch "per Hand" abrufen. Ich hoffe, dass ich bald mit einem laufenden Login aufwarten kann, sodass dem Client quasi ein User gesetzt wird.

Hierbei bin ich jetzt davon weg, eine der eingebauten Funktionen von Tomcat/Glassfish zu nutzen. Letztendlich habe ich meine Benutzerdaten in meiner Datenbank und kann diese ja auch ohne weiteres überprüfen. Ich möchte also die Sicherheitsfragen von der Serverebene auf die Applikationsebene verschieben.

Ich habe nun zwei Probleme zu lösen:
1. Alle Abfragen, die nicht über HTTPS laufen, sollen umgeleitet werden (klassischer Redirekt) - wenn möglich könnte ich das noch nach Mime Type filtern (weil z.B. Bilder und CSS/JS ruhig über normales HTTP laufen können).
Es gibt dafür die Methode response.isSecure(). Ich habe jetzt aber mehrmals gelesen, dass man das tunlichst nicht mit einem Scriptlet in JSP machen soll, sondern das ganze über ein Servlet abhandeln sollte. Es wäre ja auch notwendig, die alte Anfrage so umzubauen, dass der Inhalt (welche Datei angefragt wird), gleich bleibt, aber der Empänger auf SSL gelenkt wird. (und ggf. auf einen anderen Port).
2. Es soll vor jeder Anfrage geprüft werden, ob die SSL-Verbindung einem angemeldeten Benutzer gehört. So dies nicht der Fall ist, soll auf eine Loginseite weitergeleitet werden.

Beide Probleme haben gemeinsam, dass die Überprüfung(en) abgehandelt werden müssen, bevor überhaupt etwas anderes passiert.
Ich hatte jetzt überlegt, dass ich ein Servlet einfach auf /* mappe - also ALLE Anfragen an die Applikation über dieses Servlet laufen.
Ich muss jetzt nur rausfinden, wie ich die "guten" Anfragen (die, von eine authorisierten User) an geeignete Stelle weiterleite.
Dazu müsste ich ja u.a. direkt mein Servlet aufrufen, welches für Jersey/REST meine Anfragen abhandelt. Ein HTTP-konformes Redirect würde ja irgendwie eine Endlosschleife verursachen. Ich lese grade EJB3 in Action und bin auch schon auf ein paar interessante Stellen gestoßen. Was fehlt ist einfach noch der Aha-Effekt.

Nachbemerkung zu JavaScript:
Durch die ganzen - notwendigen - Callbacks und die Nutzung von DOM-Komponenten, kommt es mir langsam vor, als würde ich Java programmieren. Letztendlich sehr zweischneidig: 
A) Man hat Schemata, die funktionieren (weil aus Java bekannt)
B)  JavaScript verliert so ein bißchen den Charme einer leichten und Schnellen Scriptsprache.

Blogged with Flock

January 28, 2008

Upgrade - Downgrade - Löschen - Upgrade: Läuft

Mal wieder ne Stellungnahme:

Da ich u.a. eine Query machen möchte, ob zum Beispiel ein Benutzer mit einer bestimmten Email schon existiert, hatte ich bisher folgende URI zum Abfragen:

 http://localhost:8080/cotodo/resources/users/like/?cat=email&[email protected]/

Das lief auch wunderbar. Wen es interessiert, der Code dazu schaut so aus:

    @HttpMethod("GET")
    @UriTemplate("like/")
    @ProduceMime({"application/xml", "application/json"})
    public UsersConverter getLikeQuery(
            @QueryParam("val") @DefaultValue("") String val,
            @QueryParam("cat") @DefaultValue("email") String cat ) {
          try {
            return new UsersConverter(getEntitiesLike(cat, val), context.getAbsolute());
        } finally {
            PersistenceService.getInstance().close();
        }
    }

Allerdings wäre eine schönere URI ala
 http://localhost:8080/cotodo/resources/users/email/like/[email protected]/
denkbar.

Ich habe mich dann heute mal hingesetzt, um auf eine neuere JSR311 Version (Version 0.5) zu updaten. Eigentlich sollte so allmählich die Final kommen, aber nungut, man weiß ja nicht was für Fehler noch kommen mögen.
Kurzum: Es hat sich einiger geändert. Ist wohl das Problem, wenn man mit nicht spezifizierten Packeten arbeitet :-).
Hier mal ein paar Änderungen:

@HttpMethod ist verkürzt worden:

aus @HttpMethod("GET") wird @GET, aus @HttpMethod("POST") wird @POST usw.

getAbsolute() heißt nun getAbsolutePath(), ansonsten sind mir keine Änderungen aufgefallen.

Der URI Builder (eine statische Klasse, um URIs zusammen zu bauen) wurde umbenannt - was meiner Meinung nach einleuchtender ist nun:
Aus:
 Builder.created(context.getAbsolute().resolve(entity.getUserId() + "/")).build();
wird nun:
 Response.created(context.getAbsolutePath().resolve(entity.getUserId() + "/")).build();

Da in beiden Fällen der Rückgabewert vom Typ Response ist, ist letzteres einleuchtender.

Mit der neuen Version 0.5 ändert sich obige Methode folgendermaßen:

    @GET
    @Path("{cat}/like/{val}/")
    //@UriTemplate("like/")
    @ProduceMime({"application/xml", "application/json"})
    public UsersConverter getLikeQuery(
            @UriParam("val") @DefaultValue("") String val,
            @UriParam("cat") @DefaultValue("email") String cat ) {
          try {
            return new UsersConverter(getEntitiesLike(cat, val), context.getAbsolutePath());
        } finally {
            PersistenceService.getInstance().close();
        }
    }

Und tut nun auch direkt was sie soll. Ein unschönes (weil sicherheitskritisches) Problem habe ich allerdings in der getEntitiesLike Methode:
Ich muss in den JPQL-Query-String die variabel cat direkt einfügen, über parameter scheint es nicht zu gehen (ist wohl wirklich nur für variable werte vorgesehen). Ich muss noch mal suchen, ob es eine Art SQL_escape methode gibt. Ich will ja nun keine SQL-Injektion hinauf beschwören.

Abschließen hat mich das Update einen guten Teil des Morgens gekostet.
Letztendlich war wohl eine Jar version von ASM (wird u.a. von maven2 benutzt, aber von glassfish benötigt) fehlerhaft.
Es kam andauernd:
 java.lang.NoClassDefFoundError: org/objectweb/asm/ClassVisitor
Obwohl die Klasse definitiv im vorhandenen jar zu finden ist!

Letztendlich habe ich dann alle meine jars mal gelöscht und Schritt für Schritt neu hinzugefügt.
Es hat wohl tatsächlich etwas mit fehlerhaften Referencen zu tun. Das gleiche Problem triff wohl auch bei der Benutztung von Hibernate auf.

Heute Abend, oder morgen schreibe ich dann noch etwas dazu, wie ich ein skizziertes HTML Formular per JS DOM Anweisungen erstelle.
Braucht ein wenig Übung, ist aber sauberer und schneller als sich Strings zusammen zu bauen und außerdem bleibt einem in Hinsicht auf AJAX und callback functions eh nichts anderes übrig.

Blogged with Flock

January 30, 2008

OFFTOPIC: texten mit LaTeX

Es gibt 1001 Gründe, wieso ich jetzt etwas zu LaTeX unter MacOS schreibe.
Einer mag sein, dass ich grade - zufälligerweise - LaTeX intensiv nutze.
Ein weiterer, dass ich grade über einen ziemlich dummen Bug unter MacOS Leopard 10.5.1 gestoßen bin, der es einem (und vor allem grade MIR) unmöglich macht, mein schönes LaTeX-Dokument (nein ich sage NICHT was es ist) auszudrucken.
Siehe auch dazu: hier

Ach noch n Bug: Konnte man unter MacOS 10.4.x noch recht simpel mit
 ifconfig ethX lladdr
oder
 ifconfig ethX ether
noch relativ angenehm einfach die MAC Adresse seines Netdevices ändern, geht das seltsamerweise unter 10.5 nicht mehr. In den man-pages steht es aber noch. Ich benutze nun seit 10.3 n Mac und plötzlich kommen die Bugs :-/.

Noch mal zum Thema LaTeX:
Wer sich schon immer gefragt hat, wie man denn unter TexMaker oder TexShop eingetippte Umlaute in sein Latex-Dokument bekommt, der sollte es mal mit diesen Einstellungen versuchen:

 \usepackage[T1]{fontenc}
 \usepackage[applemac]{inputenc}
 \usepackage[ngerman]{babel}

Die tex-Datei sollte man dann bitte als MacOS-Latin1 abspeichern und nicht als UTF8. Gleiches gilt übrigens für alle Listing (Quellcode) Dateien.
Hat gedauert, bis ich darauf kam. Hatte die natürlich alle als UTF8 und in guter Manier alle Kommentare in Deutsch und mit Umlauten.


OFFOFFTOPIC:
Ich habe noch bissel Platz im Auto für Donnerstag. Wer noch ne Fahrgelegenheit braucht, kann sich ja einfach bei mir melden.

Ich teste jetzt noch ob der Acrobat Reader mein PDF will (und sowas unter MacOS) *grml*

Blogged with Flock

P.S.: ich vermisse irgendwie ein Wiki :-o

About January 2008

This page contains all entries posted to Philipps paper equivalent Blog in January 2008. They are listed from oldest to newest.

December 2007 is the previous archive.

February 2008 is the next archive.

Many more can be found on the main index page or by looking through the archives.

Powered by
Movable Type 3.31