update

some thoughts:

  1. The stack with “things i should post” is definitely to high :-/
    HP w2408h review
    2xAVM 7170 WDS howto
  2. I am now a proud owner of a HP w2408h – great product. Maybe i will do a short review (ref. to 1.).
    It is connected via HDMI to my MacBook
  3. Yeah! Finally our new server (current state: in pieces) is on its way. So stay tuned for a live install session. Our plan is:
    Gentoo basic system with XEN
    some VMs for hosting/playing around.
    This will be fun!
  4. I am just thinking about a smal, energy-efficient NAS-System. Nexenta-Core seems to be a great base for such a system. It hast a around 1GB Harddisk footprint! I am currently undecided between a VIA or an AMD based system, but i found some guy with knowledge and some tutorials on a Sun blog. With the chenbro ES34069 there is finally a perfect case on the market.
    I will collect some infos on the CeBIT next weekend.

Irgendwie bin ich momentan in der seltsamen Situation, viel Zeit zu haben, aber auch kaum den Kopf für andere Dinge frei zu haben, als meine Diplomarbeit abzuschließen. Ich wollte jetzt nur mal wieder das Blog mit Inhalt versorgen, weil ich sonst nur noch Spammer-Comments hier erhalten ^^. Irgendwie sammeln sich hier Ideen für Projekte und letztendlich frage ich mich ehrlich wieso, ich nicht längst mal wieder was geschrieben habe :-/.Ich werd dann mal das Blog updaten (also Software jetzt mal) 😉

SSL erzwingen in einem Java Application Server

Morgen :-),
so langsam bin ich dabei alle meine Problem gelöst zu bekommen 🙂 – ja wird auch Zeit :-D.
Bezogen auf diesem Post:
Ich bin an der Implementierung mit JAAS noch dran, allerdings habe ich herausgefunden, dass man dem Server einen Filter unterschieben kann, der bei jedem Aufruf überprüft, ob eine SSL Verbindung besteht, und wenn dies nicht der Fall ist, diese per Redirect erzwingt.
Das ganze funktioniert über Filter (wie es ja Ruby on Rails auch macht).
Man erstellt sich eine Klasse, welche javax.servlet.Filter implementiert.
Diese bindet man dann per web.xml ein:

    <filter>
        <filter-name>SSLFilter</filter-name>
        <filter-class>de.hausswolff.cotodo.security.SSLFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>SSLFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

D.h. alle Anfragen, die an die Applikation gesendet werden, werden durch diesen Filter geschickt.
Der Filter im einzelnen vergleicht ob request.getScheme() == “https” ist. ansonsten redirected er einfach auf entsprechnede Seite.
In meinem Fall (da eine nicht-https Verbindung nur bei einem nicht angemeldeten Benutzer vorhanden ist auf eine Login-Seite)
Ich hoffe, dass ich morgen dann meine Anmeldung vervollständigen kann.
Für alle die sich noch näher mit den Feature von JEE 5 befassen wollen:
Unter http://java.sun.com/developer/releases/petstore/  kann man sich mit petstore eine Beispielanwendung herunterladen, die die Neuerungen (inkl. AJAX) demonstriert. Duke’s Bank scheint demnach nicht mehr genutzt zu werden 🙂

mal wieder…. REST

Guten Morgen!
Weil ich grade darüber am schreiben bin, mal wieder was zum Thema:
How I Explained REST to My Wife

Wife: Who is Roy Fielding?
Ryan: Some guy. He's smart.
Wife: Oh? What did he do?
Ryan: He helped write the first web servers and then did a ton of research explaining why the web works the way it does.
      His name is on the specification for the protocol that is used to get pages from servers to your browser.
Wife: How does it work?
Ryan: The web?
Wife: Yeah.
Ryan: Hmm. Well, it's all pretty amazing really. And the funny thing is that it's all very undervalued.
      The protocol I was talking about, HTTP, it's capable of all sorts of neat stuff that people ignore for some reason.
Wife: You mean http like the beginning of what I type into the browser?
Ryan: Yeah. That first part tells the browser what protocol to use.
      That stuff you type in there is one of the most important breakthroughs in the history of computing.
Wife: Why?
Ryan: Because it is capable of describing the location of something anywhere in the world from anywhere in the world.
      It's the foundation of the web. You can think of it like GPS coordinates for knowledge and information.
Wife: For web pages?
Ryan: For anything really. That guy, Roy Fielding, he talks a lot about what those things point to in that research
      I was talking about. The web is built on an architectural style called REST.
      REST provides a definition of a resource, which is what those things point to.
Wife: A web page is a resource?
Ryan: Kind of. A web page is a representation of a resource. Resources are just concepts. URLs--those things that you type into the browser...
Wife: I know what a URL is..
Ryan: Oh, right. Those tell the browser that there's a concept somewhere. A browser can then go ask for a specific representation of the concept.
      Specifically, the browser asks for the web page representation of the concept.
Wife: What other kinds of representations are there?
Ryan: Actually, representations is one of these things that doesn't get used a lot. In most cases, a resource has only a single representation.
      But we're hoping that representations will be used more in the future because there's a bunch of new formats popping up all over the place.
Wife: Like what?
Ryan: Hmm. Well, there's this concept that people are calling Web Services.
      It means a lot of different things to a lot of different people but the basic concept is that machines could use the web just like people do.
Wife: Is this another robot thing?
Ryan: No, not really. I don't mean that machines will be sitting down at the desk and browsing the web.
      But computers can use those same protocols to send messages back and forth to each other.
      We've been doing that for a long time but none of the techniques we use today work well when you need to be able to talk to all of the machines in the entire world.
Wife: Why not?
Ryan: Because they weren't designed to be used like that. When Fielding and his buddies started building the web,
      being able to talk to any machine anywhere in the world was a primary concern. Most of the techniques we use at work to get computers to talk to each other didn't have those requirements. You just needed to talk to a small group of machines.
Wife: And now you need to talk to all the machines?
Ryan: Yes - and more. We need to be able to talk to all machines about all the stuff that's on all the other machines.
      So we need some way of having one machine tell another machine about a resource that might be on yet another machine.
Wife: What?
Ryan: Let's say you're talking to your sister and she wants to borrow the sweeper or something. But you don't have it - your Mom has it.
      So you tell your sister to get it from your Mom instead. This happens all the time in real life and it happens all the time when machines start talking too.
Wife: So how do the machines tell each other where things are?
Ryan: The URL, of course. If everything that machines need to talk about has a corresponding URL, you've created the machine equivalent of a noun.
      That you and I and the rest of the world have agreed on talking about nouns in a certain way is pretty important, eh?
Wife: Yeah.
Ryan: Machines don't have a universal noun - that's why they suck. Every programming language, database, or other kind of system has a different way of talking about nouns.
      That's why the URL is so important. It let's all of these systems tell each other about each other's nouns.
Wife: But when I'm looking at a web page, I don't think of it like that.
Ryan: Nobody does. Except Fielding and handful of other people. That's why machines still suck.
Wife: What about verbs and pronouns and adjectives?
Ryan: Funny you asked because that's another big aspect of REST. Well, verbs are anyway.
Wife: I was just joking.
Ryan: It was a funny joke but it's actually not a joke at all. Verbs are important. There's a powerful concept in programming and CS theory called polymorphism. That's a geeky way of saying that different nouns can have the same verb applied to them.
Wife: I don't get it.
Ryan: Well.. Look at the coffee table. What are the nouns? Cup, tray, newspaper, remote. Now, what are some things you can do to all of these things?
Wife: I don't get it...
Ryan: You can get them, right? You can pick them up. You can knock them over. You can burn them. You can apply those same exact verbs to any of the objects sitting there.
Wife: Okay... so?
Ryan: Well, that's important. What if instead of me being able to say to you, "get the cup," and "get the newspaper," and "get the remote"; what if instead we needed to come up with different verbs for each of the nouns? I couldn't use the word "get" universally, but instead had to think up a new word for each verb/noun combination.
Wife: Wow! That's weird.
Ryan: Yes, it is. Our brains are somehow smart enough to know that the same verbs can be applied to many different nouns. Some verbs are more specific than others and apply only to a small set of nouns. For instance, I can't drive a cup and I can't drink a car. But some verbs are almost universal like GET, PUT, and DELETE.
Wife: You can't DELETE a cup.
Ryan: Well, okay, but you can throw it away. That was another joke, right?
Wife: Yeah.
Ryan: So anyway, HTTP--this protocol Fielding and his friends created--is all about applying verbs to nouns. For instance, when you go to a web page, the browser does an HTTP GET on the URL you type in and back comes a web page.
Web pages usually have images, right? Those are separate resources. The web page just specifies the URLs to the images and the browser goes and does more HTTP GETs on them until all the resources are obtained and the web page is displayed. But the important thing here is that very different kinds of nouns can be treated the same. Whether the noun is an image, text, video, an mp3, a slideshow, whatever. I can GET all of those things the same way given a URL.
Wife: Sounds like GET is a pretty important verb.
Ryan: It is. Especially when you're using a web browser because browsers pretty much just GET stuff. They don't do a lot of other types of interaction with resources. This is a problem because it has led many people to assume that HTTP is just for GETing. But HTTP is actually a general purpose protocol for applying verbs to nouns.
Wife: Cool. But I still don't see how this changes anything. What kinds of nouns and verbs do you want?
Ryan: Well the nouns are there but not in the right format.
Think about when you're browsing around amazon.com looking for things to buy me for Christmas. Imagine each of the products as being nouns. Now, if they were available in a representation that a machine could understand, you could do a lot of neat things.
Wife: Why can't a machine understand a normal web page?
Ryan: Because web pages are designed to be understood by people. A machine doesn't care about layout and styling. Machines basically just need the data. Ideally, every URL would have a human readable and a machine readable representation. When a machine GETs the resource, it will ask for the machine readable one. When a browser GETs a resource for a human, it will ask for the human readable one.
Wife: So people would have to make machine formats for all their pages?
Ryan: If it were valuable.
Look, we've been talking about this with a lot of abstraction. How about we take a real example. You're a teacher - at school I bet you have a big computer system, or three or four computer systems more likely, that let you manage students: what classes they're in, what grades they're getting, emergency contacts, information about the books you teach out of, etc. If the systems are web-based, then there's probably a URL for each of the nouns involved here: student, teacher, class, book, room, etc. Right now, getting the URL through the browser gives you a web page. If there were a machine readable representation for each URL, then it would be trivial to latch new tools onto the system because all of that information would be consumable in a standard way. It would also make it quite a bit easier for each of the systems to talk to each other. Or, you could build a state or country-wide system that was able to talk to each of the individual school systems to collect testing scores. The possibilities are endless.
Each of the systems would get information from each other using a simple HTTP GET. If one system needs to add something to another system, it would use an HTTP POST. If a system wants to update something in another system, it uses an HTTP PUT. The only thing left to figure out is what the data should look like.
Wife: So this is what you and all the computer people are working on now? Deciding what the data should look like?
Ryan: Sadly, no. Instead, the large majority are busy writing layers of complex specifications for doing this stuff in a different way that isn't nearly as useful or eloquent. Nouns aren't universal and verbs aren't polymorphic. We're throwing out decades of real field usage and proven technique and starting over with something that looks a lot like other systems that have failed in the past. We're using HTTP but only because it helps us talk to our network and security people less. We're trading simplicity for flashy tools and wizards.
Wife: Why?
Ryan: I have no idea.
Wife: Why don't you say something?
Ryan: Maybe I will.

Quelle: Ryan Tomayko’s Blog: How I Explained REST to My Wife
und hier mal ein Ausblick wie das ganze bei mir aussieht:

OFFTOPIC: texten mit LaTeX

Es gibt 1001 Gründe, wieso ich jetzt etwas zu LaTeX unter MacOS schreibe.
Einer mag sein, dass ich gerade – zufälligerweise – LaTeX intensiv nutze.
Ein weiterer, dass ich gerade über einen ziemlich dummen Bug unter MacOS Leopard 10.5.1 gestoßen bin, der es einem (und vor allem gerade 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 Net-Devices ä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 wenig 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 so etwas unter MacOS) *grml*

P.S.: ich vermisse irgendwie ein Wiki 😮

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&val=Test@test.com/
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/philipp@haussleiter.de/

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}/")
@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 Referenzen 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.

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 um zubauen, 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 autorisierten 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:

  1. Man hat Schemata, die funktionieren (weil aus Java bekannt)
  2. JavaScript verliert so ein bisschen den Charme einer leichten und schnellen Scriptsprache.

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 selbst 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 sieht so aus:


var DEBUG = {};

DEBUG.div = document.createElement("div");
DEBUG.linkdiv = document.createElement("div");
DEBUG.linkcontent = document.createElement("p");
DEBUG.JSADDs = document.createElement("script");
DEBUG.CSSADDs = document.createElement("style");

DEBUG.JSADDs.appendChild(document.createTextNode('function D_showLink(no){var obj = new Object(); var str = DEBUG.nodeLinks[no]; DEBUG.linkcontent.innerHTML = "Node #"+no+":<br />"+str+": <br />"; try{var obj = eval(str); var val = eval(str+".nodeValue"); }finally{DEBUG.linkcontent.innerHTML += "<u>"+obj.className+"</u>.<b>"+obj.nodeName+"</b>:<br />s:"+obj+"<br />v:"+obj.nodeValue;}}'));

DEBUG.CSSADDs.appendChild(document.createTextNode(' #D_out{}'));
DEBUG.CSSADDs.appendChild(document.createTextNode(' #D_link{background-color:lightyellow; border: 1px black dashed;}'));
DEBUG.CSSADDs.appendChild(document.createTextNode(' .D_nodeT1{background-color:#adadad;}'));
DEBUG.CSSADDs.appendChild(document.createTextNode(' .D_nodeT2{background-color:lightgreen;} '));
DEBUG.CSSADDs.appendChild(document.createTextNode(' .D_nodeT3{background-color:white;} '));
DEBUG.CSSADDs.appendChild(document.createTextNode(' .D_nodeT4{background-color:lime;} '));
DEBUG.CSSADDs.appendChild(document.createTextNode(' .D_nodeT8{background-color:white;} '));
DEBUG.CSSADDs.appendChild(document.createTextNode(' .D_nodeT9{background-color:#dedede;} '));



DEBUG.TagCounts = new Array();
DEBUG.nodeLinks = new Array();
DEBUG.nodeNums = 0;

DEBUG.nodeTypes = new Array();
DEBUG.nodeTypes[1] = 'Elementknoten';
DEBUG.nodeTypes[2] = 'Attributknoten';
DEBUG.nodeTypes[3] = 'Textknoten';
DEBUG.nodeTypes[4] = 'Knoten für CDATA-Bereich';
DEBUG.nodeTypes[5] = 'Knoten für Entity-Referenz';
DEBUG.nodeTypes[6] = 'Knoten für Entity';
DEBUG.nodeTypes[7] = 'Knoten für Verarbeitungsanweisung';
DEBUG.nodeTypes[8] = 'Knoten für Kommentar';
DEBUG.nodeTypes[9] = 'Dokument-Knoten';
DEBUG.nodeTypes[10] = 'Dokumenttyp-Knoten';
DEBUG.nodeTypes[11] = 'Dokumentfragment-Knoten';
DEBUG.nodeTypes[12] = 'Knoten für Notation';

DEBUG.isInit = false;
DEBUG.init = function(){
    if(DEBUG.isInit) return;
    var body = document.getElementsByTagName("body")[0];
    var head = document.getElementsByTagName("head")[0];
    var first = body.firstChild;
    var style = document.createElement("style");
    var alink = document.createElement("a"); 
    
    alink.setAttribute("name", "D_link");
    
    DEBUG.CSSADDs.setAttribute("type", "text/css");
    DEBUG.JSADDs.setAttribute("type", "text/javascript");
    
    DEBUG.div.setAttribute("id", "D_out");
    DEBUG.linkdiv.setAttribute("id", "D_link");

    DEBUG.div.innerHTML = '<p style="background-color:lightyellow; border: 1px black dashed">(<nodeType>): <u><className></u>.<b><nodeName></b> <o.toString()></p>';
    DEBUG.linkcontent.innerHTML = "nr-----------------------------------nr";
    alink.appendChild(DEBUG.linkcontent); 
    
    DEBUG.linkdiv.appendChild(alink);
    DEBUG.str = 'o';
    
    body.insertBefore(DEBUG.linkdiv, first);
    body.appendChild(DEBUG.div);
    head.appendChild(DEBUG.CSSADDs);
    head.appendChild(DEBUG.JSADDs);  

    DEBUG.isInit = true;
}

DEBUG.makeTagLink = function(str, tag){
	var out = '';
	var n = DEBUG.TagCounts[tag] || 0;
	DEBUG.nodeLinks[DEBUG.nodeNums] = str+'.getElementsByTagName("'+tag+'")['+n+']';
	DEBUG.TagCounts[tag] = n+1;	
	out = 'D_showLink('+DEBUG.nodeNums+')';
	DEBUG.nodeNums++;		
	return out;
}

DEBUG.makeAttrLink = function(ptag,attr){
	var out = '';
	DEBUG.nodeLinks[DEBUG.nodeNums] = DEBUG.nodeLinks[ptag]+'.getAttribute("'+attr+'")';
	out = 'D_showLink('+DEBUG.nodeNums+')';
	DEBUG.nodeNums++;	
	return out;
}

DEBUG.makeTextLink = function(ptag, no){
	var out = '';
	DEBUG.nodeLinks[DEBUG.nodeNums] = DEBUG.nodeLinks[ptag]+'.childNodes['+no+']';
	out = 'D_showLink('+DEBUG.nodeNums+')';
	DEBUG.nodeNums++;	
	return out;
}

DEBUG.showParse = function(o, str){
    DEBUG.init();
    DEBUG.div.innerHTML += '<p style="background-color:skyblue; border: 1px black dashed"><u>'+o.className+'</u>.<b>'+o.nodeName+'</b></p>';
    DEBUG.div.innerHTML += DEBUG.parse(o, str);
}

DEBUG.moveDIV = function(x,y){
	DEBUG.div.style.position = 'absolute;';
	DEBUG.div.style.top = ''+x+'px;';
	DEBUG.div.style.left = ''+y+'px';
}

DEBUG.parse = function(o, str){
	if (typeof str == "undefined" || !str) str = 'o';
        if (typeof o == "undefined" || !o) o = document;
	var out = '<dir style="list-style-type:none">';
	if(o === DEBUG.div) return out +'</dir>';
	
	out += '<li class="D_nodeT'+o.nodeType+'">│<br />│('+DEBUG.nodeTypes[o.nodeType]+'): <u>'+o.className+'</u>.<b>'+o.nodeName+'</b> '+o.toString()+'<a onClick="'+DEBUG.makeTagLink(str, o.nodeName)+'" href="#D_link">()</a></li>';
	
	var ptag = DEBUG.nodeNums-1;
	if(o.hasAttributes()){
            var count = o.attributes.length || 0;
            out += '<li class="D_nodeT'+o.nodeType+'">';
            out += '├'+count+' attribute(s)';
            out +='<dir style="list-style-type:none">';
            for(var i=0; i < count; i++){
                    out += '<li class="D_nodeT'+o.attributes[i].nodeType+'">├('+DEBUG.nodeTypes[o.attributes[i].nodeType]+'): <u>'+o.attributes[i].className+'</u>.<b>'+o.attributes[i].nodeName+'</b> » '+o.attributes[i].nodeValue+'<a onClick="'+DEBUG.makeAttrLink(ptag, o.attributes[i].nodeName)+'" href="#D_link">()</a></li>';
            }
            out += '</dir>';
            out +='</li>';		
	}
	

	if(o.hasChildNodes()){
            var count = o.childNodes.length;
            out += '<li class="D_nodeT'+o.nodeType+'">';
            out += '├'+count+' child(s)';
            out +='<dir style="list-style-type:none">';
            for(var i=0; i < count; i++){
                if(o.childNodes[i].childNodes.length > 0){
                    out += '<li class="D_nodeT'+o.childNodes[i].nodeType+'">'+DEBUG.parse(o.childNodes[i],str)+'</li>';
                }else{
                    if((o.childNodes[i].nodeName == '#text' || o.childNodes[i].nodeName == '#comment') && o.childNodes[i].data.length > 0){
                            out += '<li class="D_nodeT'+o.nodeType+'">│('+DEBUG.nodeTypes[o.childNodes[i].nodeType]+'): <u>'+o.childNodes[i].className+'</u>.<b>'+o.childNodes[i].nodeName+'</b> '+o.childNodes[i].toString()+'<a onClick="'+DEBUG.makeTextLink(ptag, i)+'" href="#D_link">()</a>';				
                            out += '<br />├<span class="D_nodeT'+o.childNodes[i].nodeType+'" style="border: 1px black dashed">'+o.childNodes[i].data+'</span>';	
                    }else{
                            out += '<li class="D_nodeT'+o.nodeType+'">│('+DEBUG.nodeTypes[o.childNodes[i].nodeType]+'): <u>'+o.childNodes[i].className+'</u>.<b>'+o.childNodes[i].nodeName+'</b> '+o.childNodes[i].toString()+'<a onClick="'+DEBUG.makeTagLink(str, o.childNodes[i].nodeName)+'" href="#D_link">()</a>';				
                    }	
                    out += "</li>";
                }
            }
            out += '</dir>';
            out +='</li>';		
	}
	
	out += '</dir>';
	return out;
}

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 id="3" uri="http://localhost:8080/cotodo/resources/users/3/">
    <ul>
        <li id="email">Test3</li>
        <li id="forename">Test</li>
        <li id="surname">NeuerTester</li>
    </ul>
    <ul style="display:none" uri="http://localhost:8080/cotodo/resources/users/3/groups/">
        <li>1</li>
        <li>2</li>
    </ul>
    <ul style="display:none" uri="http://localhost:8080/cotodo/resources/users/3/outbox/">
        <li>2</li>
    </ul>
    <ul style="display:none" uri="http://localhost:8080/cotodo/resources/users/3/inbox/">
        <li>2</li>
    </ul>
    <ul style="display:none" uri="http://localhost:8080/cotodo/resources/users/3/created/">
        <li>1</li>
        <li>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 optimierten 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><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><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><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><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” /> bedeutet, dass der Wert aus der Hirachie user/ geholt 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 sind folgende Links:

und zu guter Letzt:

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 User-Service 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 distanziert 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 innerhalb 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 – zugegebenermaß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 User-Service  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.