Plotting UNIX Processes with DOT

Flattr this!

Inspired by this Post, i started playing around with ps, nodejs and GraphViz.

After reading some ps man Pages, i found the necessary ps parameters.
For MacOS i used

ps -A -c -o pid,ppid,pcpu,comm,uid -r

For Linux i used

ps -A -o pid,ppid,pcpu,comm,uid

You then get some Output like:

    PID    PPID %CPU COMMAND           UID
      1       0  0.0 init                0
      2       1  0.0 kthreadd            0
      3       2  0.0 migration/0         0
      4       2  0.0 ksoftirqd/0         0
      5       2  0.0 migration/0         0
      6       2  0.0 watchdog/0          0

So you are getting the ProcessID, the Parent ProcessID, CPU Usage (i am not using for plotting atm), the Command and the UserID.
I created a simple Node Script, that you can run either directly under MacOS (for all other Unices you need to update the ps command).
Or you can give the script a previous generated ps output for parsing:

plotPS.sh /tmp/host.log > /tmp/host.dot

The resulting DOT Code is then Piped into a DOT File.

Here are some examples:

My MacOS Laptop:

MacBook Pro
bigger

A Sinlge Linux Host with Dovecot and Apache2/Passenger:

Apache2 / Mail Server
bigger

A Linux Host with OpenVZ and KVM Instances:

OpenVZ / KVM Host
bigger

In the original Post, there were also Dependencies between CPU Usage and Size of the Graphical Nodes, also it would be more useful to only plotting the processes of one VM from its inside.
But i guess for one evening the result is okay :-).

Hacking just for Fun: using Bookmarklets

Flattr this!

So there are a handful of webtools using Bookmarklets for their services. The first i know was del.icio.us for saving a Webpage to your del.icio.us bookmarks. Another famous service is Instapaper (it uses internally read it later pocket, but that is another Story). I have a special service in mind, i want to create using a Bookmarklets, before i start, i played around with the Bookmarklet from Instapaper.

The Magic is just a normale HTML A Tag, in whith some javascript is embedded:

javascript:function%20iprl5(){var%20d=document,z=d.createElement('scr'+'ipt'),b=d.body,l=d.location;try{if(!b)throw(0);d.title='(Saving...)%20'+d.title;z.setAttribute('src',l.protocol+'//www.instapaper.com/j/foobar?u='+encodeURIComponent(l.href)+'&t='+(new%20Date().getTime()));b.appendChild(z);}catch(e){alert('Please%20wait%20until%20the%20page%20has%20loaded.');}}iprl5();void(0)

If we unwrap that script we got

function iprl5(){
    var d=document,
    z=d.createElement('scr'+'ipt'),
    b=d.body,
    l=d.location;
    try{
        if(!b)
            throw(0);
        d.title='(Saving...) '+d.title;
        z.setAttribute('src',l.protocol+'//www.instapaper.com/j/foobar?u='+encodeURIComponent(l.href)+'&t='+(new Date().getTime()));
        b.appendChild(z);
    }catch(e){
        alert('Please wait until the page has loaded.');
    }
}
iprl5();
void(0)

The command basically just creates a script tag in the active DOM-Tree and preloads some Javascript-File. The File is then executed by the Browsers JS Runtime.
I started a litte Demo Project. You find it here on GitHub. It is based on Play! 1.2.4 (Installation Guide here).

ATM there are just two JS Templates. The first one (app/views/Application/bookmarklet.js) just contains the source for the Script-Tag itself. The second (app/views/Application/input.js) will be loaded then after the Javascript behinde that link is called.

Using UIAutomation for Multilanguage iOS Applications

Flattr this!

With the appearance of iOS 4.0 Apple introduced a new Test-Framework for automatically UI Testing: UI Automation. Based on Javascript and build-in into Instruments, UI Automation is a very useful tool during the Developing of iOS Application.
A very good introduction in UIAutomation is here and here.
During the development of a iOS Application, we decided to port it to iOS 4.0 and therefor use also UIAutomation for regression testing (before that we used GHUnit Tests for Component Testing – but thats another story).
As we are primarily a company dealing with web-based application, we had almost zero afford to deal with the Javascript syntax of UI Automation. But we had to deal with the fact, that we developing a dual language Application (de and en), and therefore need a possibility to test the whole UI in both languages.
If you are familiar with UI Automation, you probably know that the Framework uses the accessibility labels of your UI and also often Button Labels. So you have to deal with the actual language of the current UI Setting. But wait. There is already a valid mapping of different language to a given key. If you internationalize your application you will use so called Localizable.strings to do your language Mapping (more here).
So we just need a way to move our already existing Mapping into our UI Automation world. UI Automation supports the import of separate JavaScript Files to use your own Libraries and Settings. So i build a conversation script to translate your different Localizable.strings to JavaScript and moving all languages into one big collection.
So for example a String like this:

    "Library" = "Bibliothek";
    "Shop" = "Kiosk";

Will be converted to:

UIA.Localizables = {
    "de":{
        ...
        "Library" : "Bibliothek",
        "Shop" : "Kiosk",
        ...
    },
    "English":{
    }
    ...
}

The next step is to determine during your UIAutomation Test which language Setting you need to Load from your Localization File.
It is possible to readout some System Settings during an UIAutomation Test. The basic functions to find your current language and to read the correct language Array look like this:

UIA.getCurrentLang = function(){
    if(application.preferencesValueForKey("AppleLanguages")[0]  == "en")
        return "English";
    else
        return application.preferencesValueForKey("AppleLanguages")[0];
}
UIA.getCurrentLocalizables = function(){
    return UIA.Localizables[UIA.getCurrentLang()];
}
var Localizable = UIA.getCurrentLocalizables();

The first function is necessary to capture a quirk of the recent Xcode Versions (some people calling it a bug 🙂 ).
So now we can just use our String within our Test-Cases.

#import "lib/Localizables.js"
function delay(seconds){
    UIATarget.localTarget().delay(seconds);
}
function tapTab(name){
    var window = UIATarget.localTarget().frontMostApp().mainWindow();
    window.tabBar().buttons()[name].tap();
}
var window = UIATarget.localTarget().frontMostApp().mainWindow();
tapTab(Localizable['Library']);
delay(1);
tapTab(Localizable['Shop']);
delay(7);

I attached the conversion script to this post.
You just need to alter the source and destination folders of your i18n files and the UIAutomation-Tests directory.
Download file

Philipps 5 mins: Graph-Fun with AJAX and Canvas

Flattr this!

I always searched for an efficient way add dynamic diagrams to a web-project without using flash or other plugin-based magic.
With the support of the canvas tag element in almost all mainstream browser, i thought it would be a good time for creating a short demo how things workout.
You will need at least two Parts for this demo. First of all you will need a Source JSON feed. For this demo i just hacked together a very basis PHP script:

<?php
header('Content-type: application/json');
echo'{';
echo '"value":"' . rand(0, 60) . '"';
echo '}';
?>

The result is something like:

{"value":"34"}

Secondly you need a Webpage, where you want to insert your canvas element, load the data from the json feed and draw the changing values to the canvas element.
For a better performance, we will implementing pulling the data and drawing the data within two parallel cycles. The Common data Storage will be an array of 300 value (for our diagram with a width of 300px).
We are using two additional JS Files. The first we need for creating our XHTTPRequest Object and handling the response within a callback method. The second script is for parsing the JSON Feed as a Javascript Object in a safe way (an ordinary eval works, but is to unsecury).
Our main-script works in several steps:
First we initialize an array with empty elements:

function init(){
    for(var i=0; i < 300; i++){
        randomValues[i] = 0;
    }
}

 


This step is optional, but then you have a nice “zero line” at the beginning.

Secondly we have a method, that pushes a new value to the existing array, and drops the first entry, if the length of the array is greater than 300.

function addValue(arr, value){
    if(arr.push(value) > 300){
        arr.shift();
    }
}

 


The next two methods are necessary for sending our ajax-request and for handling the response in a callback method.
Basically the callback method just calls the addValue method.

The timeout variable is set to 200 ms. So the script calls our backend periodically every 200 ms and then adds a new value to our array.

function pullValue(){
    sendRequest('random.php',handleRandomRequest);
    setTimeout(pullValue, timeout);
}

function handleRandomRequest(req) {
    var text = JSON.parse(req.responseText);
    addValue(randomValues, text.value);
}

The last method is for the drawing functionality:

function draw(){
    ctx.clearRect(0, 0, 300, 60);
    ctx.fillStyle = "rgba(101,101,101, 0.5)";
    ctx.fillRect (0, 0, 300, 60);
    ctx.lineWidth = 1;
    ctx.strokeStyle = 'blue';
    ctx.beginPath();
    ctx.moveTo(1, 60-parseInt(randomValues[0]));
    for (var i=1; i<randomValues.length; i++){
        value = 60-parseInt(randomValues[i]);
        ctx.lineTo(i,value);
    }
    ctx.stroke();
    setTimeout(draw, timeout);
}

ctx is a 2d context of the canvas element.
On every call of the draw method, all elements of the array are painted. The first element is always the start point.
Because the canvas coordinate system has the point 0,0 in the upper left corner but the 0,0 point of our diagram should be in the lower left corner, you have to subtract the array-values from 60 to get the right drawing coordinate.
This method also runs periodically every 200 ms. But it also works for two times for pulling the data an drawing it.

Here you can see the script in action

Splines mit dem HTML Canvas Tag

Flattr this!

Splines innerhalb eines Canvas Tags sind mit ein wenig Javascript recht schnell machbar! (drumherum dann noch den ganzen HTML-Kram denken):

<canvas width=”150″ height=”150″ id=”myCanvas1″></canvas>
<script type=”text/javascript”>
function draw1(){
    var myCanvas1 = document.getElementById(‘myCanvas1′);
    myCanvas1.style.border = “1px black dashed”;   

    var ctx = myCanvas1.getContext(’2d’);

    ctx.beginPath();   
    ctx.strokeStyle = “#dedede”;
    for (i=1;i<15;i++){
        ctx.moveTo(i10,0);
        ctx.lineTo(i10,150);
    }
    for(j=1;j<15;j++){
        ctx.moveTo(0,j10);
        ctx.lineTo(150,j10);
    }
    ctx.stroke();   

    //Steuerpunkte malen (nur zur Verdeutlichung)
    ctx.beginPath();
    ctx.lineWidth = 2;
    ctx.strokeStyle = “rgb(0,200,0)”;
    ctx.moveTo(0,135);
    ctx.lineTo(60, 130);
    ctx.arc(60,130,3,0,360, false);
    ctx.lineTo(75,75);
    ctx.lineTo(90,20);
    ctx.arc(90,20,3,0,360, false);
    ctx.lineTo(150, 15);
    ctx.stroke();

    //quadraticCurveTo malen
    ctx.beginPath();
    ctx.strokeStyle = “#000000″;
    ctx.moveTo(0,135);
    ctx.lineWidth = 4;
    ctx.quadraticCurveTo(60, 130, 75, 75);
    ctx.moveTo(75,75);
    ctx.quadraticCurveTo(90, 20, 150, 15);
    ctx.stroke();

}
draw1();
 </script>

Das macht einem dann ein:

Das ganze funktioniert mit Firefox ab Version 1.5 (aber erst ab 2.x gehen auch quadratic Splines), Opera 9 und neueren Safaries.
Infos hier:

Die grünen Linien habe ich nur gemacht, damit man die Stützpunkte der Splines besser sehen kann.
Ach was war die CG-Vorlesung doch schön 😀

Blogged with the Flock Browser

1. GUI und die Sicherheit

Flattr this!

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

Flattr this!

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;
}

Die Nebenläufigkeit

Flattr this!

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.

Zum Jahresende

Flattr this!

Moin…
bevor das Jahr zu Ende geht, und es so scheint, als würde ich nichts tun:
Ich bin momentan dabei nach Patchwork Manie meine Texte zusammen zu schreiben – denke aber, dass ich da mal ein paar Tage konzentriert dran sitzen sollte, um mal eine Grundstruktur zu finden.
Weiterhin bin ich momentan dabei, mir einen Javascript Client zu bauen. Wie vielleicht einiger wissen, benutze ich da das YUI Framework. Dennoch ist es z.B. notwendig, die Entities, welche auf Serverseite (u.a. die JPA POJOs) existieren, auch auf Client-Seite abzubilden. Ich bin also momentan dabei, mir mein User.Task und Group Objekt, nebst Abhängigkeiten aufzubauen. Da ich nicht wegen jeder Abhängigkeit einen neuen HTTPRequest senden möchte, speichere ich mir die Objekte in separate Arrays zwischen und lade die Daten dann nur nach Bedarf nach. Das  klappt auch soweit ganz gut, aber letztendlich fehlt es mir irgendwie an den JS Eigenheiten in Bezug auf OOP Programmierung :-D.
Sobald mein Client mit den Grundfunktionen steht, werde ich mich wieder dem Thema Autorisierung/Authentifizierung über HTTP-AUTH und SSL befassen.
Ist irgendwie ein Riesenbereich und meine bisherigen Erfahrungen sagen mir, dass man es entweder ganz oder gar nicht machen sollte :-).

Alleine das Thema Rollenverteilung kann ja schon Bücher füllen. Ich hadere noch ein wenig damit, eine geeignete Lösung zu finden, um alle Request, die nach “/” gehen an ein Formular weiterzuleiten, welches dann über SSL eine Anmeldung realisiert. Weiterhin denke ich, dass ich zumindest alle Anfragen durch eine eigene EJB schleusen müsste, um jeweils User-Daten auf Serverseite anhand der SSL Session zu finden.
Neben dem eBook EJB in Action fand ich u.a. hier weitere Informationen zum Thema. Wie ich bisher ja schon geschildert habe, realisiere ich den notwendigen realm für die User-Daten meines Java Application Servers über Views in der Datenbank. Letzendlich bleiben aber auch hier noch Fragen offen:

  1. Gibt es eine Möglichkeit, alle notwendigen Einstellungen ohne Admin Konsole zu erstellen (also quasi über eine Build Script, welches dem Server dann einen Sec Realm darbietet)
  2. Ist das ganze Server unabhängig (also z.B. auf Tomcat und Glassfish beidermaßen gleich lauffähig)
  3. (zählt auch eher zu 2) welche der vielen Config Dateien gilt es jetzt zu erweitern ( neben einer web.xml gibt es diverse sun-*.xml)

Ist also noch viel zu tun – nur schön, dass es sich mal wieder herausgestellt hat, dass der JS Anteil relativ leicht von der Hand geht. Es muckt zwar hier und da noch ein wenig auf, aber das bekommt man dann auch recht schnell in den Griff.
Ich wünsche allen ein erfolgreiches 2008 und verweise dann hiermit noch mal auf meinen Post in meinem privaten Blog. (ich kann mir nicht helfen, aber letztendlich muss ich am Schriftbild da noch mal arbeiten :-D)