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.

Zum Jahresende

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)

AVM Box: Telefonbuch

Ich plane in der Zeit zwischen Weihnachten und Neujahr ein wenig am Netzwerk hier umzubauen. Es liegen definitiv zu viele Kabel hier in der Gegen herum.

Letztendlich entschied ich mich für 2 AVM Fritz Boxen (jeweils 2x7170er). Eine würde zwar reichen für Telefonanlage und Internetzugang, allerdings hat man dann zur Not einen zweiten Router zur Verfügung, falls man dies denn haben möchte.

Eine genauere Beschreibung, wie und der aktuelle Stand und der Umbau dann ablaufen wird, kommt dann später, wenn die zweite Box ankommt.Ich plane beide Boxen mit WDS verbinden. Während auf einer Box dann die Telefoniesachen laufen werden, nutzt ich die andere, um das Wlan zu erweitern (damit ich dann mein – hoffentlich bald kommendes AVM Fritz Mini nutzen kann) und um 1 PC und einen Drucker per Ethernet ins Netz einzubinden.

Alles in allem werde ich wohl so 40 m Kabel sparen ^^.

Wieso ich jetzt schon poste? Nunja irgendwie habe ich dann doch schon ein wenig mit dem Teil rum gespielt. Die neueste Firmware drauf gespielt.

Und mich in der Oberfläche umgesehen.Alles in allem sehr schön aufgeräumt und sehr viele Möglichkeiten im Vergleich zu meinem aktuellen (und hin und wieder abstürzenden) SMC Barricade 7004.Da ich mittlerweile 100% mit MacOs laufe (habe zwar noch ne VMware mit Win XP – aber nur für Notfälle), habe ich dann von den ganzen Windows Tools Abstand genommen.

Das Teil hat ja eine Weboberfläche, dass sollte also reichen. Was nun ziemlich ärgerlich ist, ist dass mein Bestreben, meine Adressen an einer zentralen Stelle zu verwalten, einen eigenem, zentralem, Telefonbuch innerhalb der FritzBox gegenüber steht.

Das kann man auch nur manuell per Web-Oberfläche füllen. Mit Hilfe von WireShark und ein paar Testläufen bin ich zu folgendem Trafficschnippsel gekommen:

 Pä˜ÿÿ×POST /cgi-bin/webcm HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X; de-de) AppleWebKit/523.10.5 (KHTML, like Gecko) Version/3.0.4 Safari/523.10.6
Content-Type: application/x-www-form-urlencoded
Referer: http://fritz.box/cgi-bin/webcm
Accept: text/xml,application/xml,application/xhtml+xml,text/html;  q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
 
Accept-Language: de-de
Accept-Encoding: gzip, deflate
Content-Length: 1292
Connection: keep-alive
Host: fritz.box
 
ªâjGK �N��N���Jè¨r�Roé4�E�@ƒ=@�@ÍÀ¨²À¨²Öâ�PÙ1Gb+€ÿÿw>
Pä˜ÿÿ×getpage=..%2Fhtml%2Fde%2Fmenus%2Fmenu2.html&……
ÿÿ×APä˜HTTP/1.0 200 OK
Cache-Control: no-cache
Content-type: text/html
Expires: -1
Pragma: no-cache
 

Hierbei ist vor allem der letzten Absatz interessant (habe ich mal gekürzt, weil man eh mit den Sonderzeichen nicht viel lesen konnte).

Es handelt sich um einen einfachen HTTP POST Befehl. Sämtlich Daten werden URL-endcoded übertragen.

Mit ein wenig PHP (im wesentlichen den String URL-decoden und nach &,= splitten) erhalte ich für einen Eintrag nun folgende Schlüssel/Werte Paare:

 getpage = ../html/de/menus/menu2.html

errorpage = ../html/de/menus/menu2.html

var:lang = devar:pagename = fonbuch

var:errorpagename = fonbuch2

var:menu = home

var:pagemaster = fonbuch

time:settings/time = 1198187178,-60

var:showall =

var:showStartIndex =

var:PhonebookEntryNew = Entry0

var:PhonebookEntryXCount = 0

var:PhonebookEntryNewCode = 01

var:PhonebookEntryNumber =

telcfg:settings/Phonebook/Entry0/Name = TESTERT TESTEREI

telcfg:settings/Phonebook/Entry0/Category = 1

telcfg:settings/Phonebook/Entry0/Number0/Type = hometelcfg:settings/Phonebook/Entry0/Number0/Number = 12345678

telcfg:settings/Phonebook/Entry0/Number0/Code = 01

telcfg:settings/Phonebook/Entry0/Number0/Vanity =

telcfg:settings/Phonebook/Entry0/Number1/Type = mobile

telcfg:settings/Phonebook/Entry0/Number1/Number = 87654321

telcfg:settings/Phonebook/Entry0/Number1/Code =

telcfg:settings/Phonebook/Entry0/Number1/Vanity =

telcfg:settings/Phonebook/Entry0/Number2/Type = work

telcfg:settings/Phonebook/Entry0/Number2/Number =

telcfg:settings/Phonebook/Entry0/Number2/Code =

telcfg:settings/Phonebook/Entry0/Number2/Vanity =

telcfg:settings/Phonebook/Entry0/DefaultNumber = 0

So sieht besagte PHP Seite aus:

Die Kombination telcfg:settings/Phonebook/Entry0/Name macht definitivLust auf mehr :-). Aber momentan fehlt mir einfach die Zeit. Aber es wäre zum Beispiel denkbar, ein kleines Java Programm zu schreiben, dass die Kontaktdaten mit dem Adressbuch synchronisiert.

Posted in PHP

Sicherheit über JAAS mit einer MySQL Datenbank

Ich gebe zu, ich habe es lang vor mir her geschoben:

Neben all der schönen REST-fähigen Implementierung, ist Sicherheit ein Bereich, der bisher ein wenig zu kurz kam. Zwar habe ich schon eine schöne Möglichkeit gefunden, wie man mit YUI eine Anmeldung über HTTP AUTH hin bekommt, aber letztendlich fehlt(e) bisher die komplette Serverseite.

Ich habe jetzt einiger Zeit gesucht und bin zu dem Ergebnis gekommen, dass mir der Java Application Server (sei es Tomcat, Glassfish o.a.) bereits mehrere JAAS basierte Module zur Verfügung stellt.
Diese Entscheidung traf ich (da es hierbei sicherlich auch Einschränkungen einzugehen gilt) u.a. nach dem Lesen dieses Artikels.

Darin wird einem abgeraten, eine Logion/Session Verwaltung in Eigenregie zu erstellen, da hierdurch teilweise ein Sicherheitsrisiko eingegangen wird.

Nach weitere Suche kam ich zu einem interessanten Artikel von Shing Wai Chan, in dem er die Einrichtung eines JDBCRealm auf einem GlassFish Server beschreibt. Bis auf das die Standard digest-Algorithmen auf den verschiedenen Application Servern unterschiedlich sind, sollte dies unter den verschiedenen Systemen lauffähig sein.

Aufgrund der Übersichtlichkeit und da ich ungern Systeme in meinen Datentabellen herumfuhrwerken lasse, habe ich mir für die entsprechend benötigten Tabellen 2 Views angelegt:

CREATE ALGORITHM = TEMPTABLE VIEW usertable AS
SELECT u.email AS userid, u.password
FROM Users u
CREATE  ALGORITHM = TEMPTABLE VIEW grouptable AS
SELECT u.email AS userid, g.name
FROM Groups g, Users u, UsersGroups us
WHERE us.user_id = u.user_id
AND g.group_id = us.group_id

Damit kann ich weiterhin sicher sein, dass der Server auch tatsächlich auf die Standard-Variablen zugreifen kann. Natürlich bin ich damit den Nachteil eingegangen, dass ein Benutzer hier nur in einer Gruppe sein kann.
Ich denke aber, dass dies zu verkraften ist, da es sich hierbei nur um den Login handelt und hierbei eigentlich nur interessant ist, ob der Benutzer als Admin oder als User angemeldet wird.
Als Standardgruppen sind in der Datenbank admins und users gesetzt.
So habe ich in meiner Config-Datei (sun-web.xml) folgende Zeilen eingefügt:

<security-role-mapping>
    <role-name>cotodo-admin-login</role-name>
    <principal-name class-name="Users">admin</principal-name>
    <group-name>admins</group-name>
</security-role-mapping>
<security-role-mapping>
    <role-name>cotodo-user-login</role-name>
    <principal-name class-name="Users">admin</principal-name>
    <group-name>users</group-name>
</security-role-mapping>

Also Session Methode werde ich mich dann wohl für eine Cookie basierte Session entscheiden, weil eine URL-encodierte Session die schönen REST-URIs verschandeln würde und dadurch auch den Austausch mit YUI erschweren mag.
Meine beiden letzten Punkte auf der Agenda (die ich diese Jahr noch durch bekommen will (!!!), sind folgende:

  1. Wie gehe ich sicher dass ein User umgeleitet wird, wenn er eine ungültige Session hat und sich einen XML-Resource Stream besorgt? (letztendlich wird es da sicherlich entsprechende Methoden geben, welche vor allen anderen Aufrufen prüfen können, ob Autorisierung und Authentisierung korrekt sind.
  2. HTTPS. Es war erstaunlich einfach, dem Server klar zu machen, dass ich gerne HTTPS benutzen möchte (einfach andere URL :-)) .

Allerdings sollte man nun sicher sein dass:

  1. Alle Anfragen auf HTTP geblockt werden (oder noch besser: auf eine Login-Seite umgeleitet werden)
  2. Sicher gestellt wird, dass sich ein User nur über eine SSL Verbindung anmelden kann (sicherlich auch durch eine Weiterleitung möglich)

simple Step by Step JPA-JAXP-REST

Da ich mir ja langsam mal Gedanken machen sollte, wie ich meine Arbeit strukturiere. Habe ich mal angefangen, beispielhaft, die Schritte zu sammeln, welche notwendig sind, um eine DB Table mit Hilfe der Java Möglichkeiten REST fähig zu machen.
Ich gedenke dies in 3 Teile zu tun:

  1. JPA Entity: Wie wird aus einer Datenbank Tabelle ein Java Objekt
  2. JAXP: Wie bekomme ich von meiner JPA Entity möglichst einfach eine Repräsentation in XML (JSON geht auch, aber erstmal reicht mir XML).
  3. JSR311/Jersey: Wie gelingt, es die Ressourcen an bestimmte URIs zu binden?

Hier dann als mal Teil 1:

Als allererstes möchte ich die folgende Datenbank Tabelle als ein einfaches POJO (Plain Old Java Object) abbilden und die Annotations machen, welche notwendig sind, damit JPA auch weiß, wie es mit der Klasse umzugehen hat.
Folgende Tabelle:

TABLE Users
===================================================
[ user_id | email | forename | surname | password ]
===================================================

Die nachfolgende Klasse ist dann das passende POJO dazu:

/*
ne ganze menge imports:
*/
import de.hausswolff.cotodo.tools.Tools;
import java.io.Serializable;
import java.util.Collection;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
/*
Leider scheint der JPA Layer verschiedene Schlüsselwörter für sicher selbst in Anspruch zu nehmen. 
"User" gehört scheinbar auch dazu.
Ich musste also diese Klasse hier dann Users nennen und blieb bei den Entitäten nun bei dem Plural.
*/
@Entity
@Table(name = "Users")
public class Users implements Serializable {
    @Id
    @Column(name = "user_id", nullable = false)
    private Integer userId;

    @Column(name = "forename", nullable = false)
    private String forename;

    @Column(name = "surname", nullable = false)
    private String surname;

    @Column(name = "email", nullable = false)
    private String email;

    @Column(name = "password", nullable = false)
    private String password;

    /*
    Hier kommt dann noch eine Reihe von den Beziehungen der Entitäten untereinander. 
    Das lasse ich hier jetzt auch mal weg, weil es zum Thema keine Rolle spielt. 
    Als Beispiel mal eine Beziehung zu der Table Tasks über folgende Tabelle:
    TABLE UsersTasks
    ===============================
    [ task_id | user_id | send_to ]
    ===============================
    Hierbei ist "task_id" die ID des Tasks ;-). user_id ist die ID des Users, welcher den Tasks erstellt hat. 
    send_to enthält die ID des Users, an den der Task als Auftrag geschickt worden ist. 
    Da es n Empfänger seien sollen, muss das ganze natürlich in eine externe Tabelle ausgelagert werden.
    */

    @ManyToMany
    @JoinTable(name="UsersTasks",
    joinColumns=@JoinColumn(name="user_id"),
    inverseJoinColumns=@JoinColumn(name="task_id"))
    private Collection SendTaskCollection;

    @ManyToMany
    @JoinTable(name="UsersTasks",
    joinColumns=@JoinColumn(name="send_to"),
    inverseJoinColumns=@JoinColumn(name="task_id"))
    private Collection ReceivedTaskCollection;

    /*
    als (quasi virtuelle) Attribute erhält jede Users Klasse nun zwei Collections, 
    welche halt nicht direkt in der Users Tabelle gespeichert werden, 
    aber durch die Relationen in der UsersTasks Tabelle abgelegt sind.
    */
    public Users() {

    }
    public Users(Integer userId) {
        this.userId = userId;
    }

    public Users(Integer userId, String forename, String surname, String email, String password) {
        this.userId = userId;
        this.forename = forename;
        this.surname = surname;
        this.email = email;
        this.password = Tools.md5(password);
    }
    /*
    Es folgt nun die übliche Anzahl an gettern/settern. Hierbei habe ich mich entschlossen, 
    nicht stumpfsinnig alle Attribute per set zu öffnen, sondern wirklich nur jene, bei denen es notwendig ist. 
    Die beiden Listen aus den obigen relationen sollten nur lesbar sein. Der Empfänger/Ersteller werden im Task direkt angegeben.
    Als Beispiel hier mal setter/getter für das Passwort:
    */

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = Tools.md5(password);
    }

...

}

Ich habe vieles aus der Klasse weg gelassen, was nicht unbedingt für die Funktion von JPA notwendig ist, aber durchaus der besseren Lesbarkeit dient. Wichtig ist, dass es einen Standard-Konstruktor gibt.
Weiter geht es mit JAXP. Also der Repräsentation von obiger Klasse in XML.

Abbilden der Relationen

Nachdem ich alle notwendigen Relationen in der Datenbank abgebildet habe, habe ich jetzt meine Annotations zwischen den Objekten fertig gestellt.
Es gibt ein paar interessante Dinge, so zum Beispiel eine rekursive Relation innerhalb von 2 Tasks (Aufgaben). Das heißt, eine Aufgabe kann n Unteraufgaben haben.


Weiterhin enthält jede Aufgaben ein Array von Empfängern und einen Sender.
Ich sehe grade, dass müsste in dem Fall kein Array sein.
Also ist das eine ManyToOne/OneToMany Beziehung. Werde ich dann noch ändern.

Ich war natürlich wieder einmal vorschnell. Hatte ich ja nicht etwas falsch gemacht
(sowas mache ich ja nicht :D), sondern schlicht etwas vergessen. ich spare mir jetzt
mal, die neuen UMLs hoch zu laden ;-). Letztendlich war die eine ManyToMany
Beziehung zwischen Users und Tasks natürlich die Leute, die Tasks an andere User
weiter delegieren. Das können ja pro Task n sein. Hat alles seine Richtigkeit. Die Welt
steht noch und ich gehe schlafen ^^. Was fehlte war einfach eine
OneToMany/ManyToOne Beziehung zwischen Tasks und Users!
Als letztes kann eine Gruppe (nun habe ich so doch mit rein genommen) 1 bis n Benutzer haben. Genauso kann ein Benutzer in 1 bis n Gruppen sein.
Eine Gruppe wird immer neu erstellt und entsprechender Benutzer hineingepackt, wenn eine Aufgabe mit einem Projekt-Flag (dazu später mehr) zu entsprechendem Benutzer zu geordnet wird. D.h. wenn besagter Benutzer Ersteller oder Verantwortlicher der Aufgabe (und deren Unteraufgaben) ist.
Hier mal das UML der Entitäten:

Wenn Interesse daran besteht, wie man die Abbildung im Einzelnen (im Code) abbildet, kann ich dazu auch noch etwas schreiben.
Und abschließend noch mal eine Übersicht aller Klassen, die ich bislang so habe.
Hierbei steht das E für Entität. Ein S steht für eine Systemklasse. Ein R für eine Klasse für eine einzelne REST Resource und eine R# für eine Resourcenliste.

Gedanken zu EJB3, meiner Welt und Diagrammen

Hello world!
Nachdem ich mit Gerald am vergangen Freitag Abend mich ein wenig ausgetauscht habe und wir über die unsere Ansätze in der Erstellung einer EJB3 JEE Anwendung gesprochen hatten, kamen mir doch schon ein wenig Zweifel, ob denn meine Anwendung nun vollständig rechtfertigt, ein EJB im Titel meiner Arbeit zu führen :-).
Nachdem ich mir dann noch ein mal das Buch über EJB2 und JPA (in dem leider die Konzentration arg auf JPA liegt) zur Brust genommen habe (kann man das bei Büchern so sagen?). Und auch noch mal die EJB3 Specs von Sun (das war übrigens mal JSR220) sowie eine recht gute Zusammenfassung und ein paar Tutorials (u.a. dieses hierangesehen habe, bin ich der Meinung, dass meine bisherigen Bemühungen definitiv in die richtige Richtung gingen (ist doch auch mal schön sowas).
Noch dazu weiß ich nun, dass ich JPA mit Annotation und einer Stateless Session Bean benutzen möchte. (Das Stateless resultiert einzig und alleine aus dem REST Ansatz). Dementsprechend kam mir die Idee, in meiner Arbeit auch definitiv ein paar Sequenzdiagramme benutzen möchte – da Zustandsdiagramme mir – zumindest momentan – eher witzlos erscheinen.
Als weiteren Punkt habe ich aus dem Treffen mitgenommen (sind heute irgendwie lauter Ratgeber-Formulierungen), mir endlich mal eine Gesamtübersicht über meine Funktionen zu erstellen. Da zu einem ordentlichen UseCase auch eine Beschreibung gehört (so habe ich es zumindest gelernt), poste ist jetzt hier erstmal zwei Diagramme unter Vorbehalt (der Beschreibungsteil ist noch in Arbeit und das Packet geht dann alsbald auf Tour, ganz ehrlich Phillip).
Ich habe mich dafür entschieden, jeweils zwei Diagramme, jeweils für den Bereich Aufgaben und für den Bereich Benutzer(-verwaltung) zu erstellen:

  1. Benutzer UseCase(s)

  2. Aufgaben UseCase(s)

Wie schon erwähnt: Die Beschreibungen der einzelnen Cases ist zum größten Teil fertig, da mit aber bis grad noch Dinge einfielen, die noch nicht durch die existierenden UseCases abgedeckt sind, möchte ich noch mal mindestens eine Nacht drüber schlafen. Auch ob ich nicht doch die Möglichkeit für verschiedene Benutzergruppen einbauen möchte schwebt mir noch im Kopf herum. Es wäre im Moment noch eine kleinere Änderung in der Datenbank und würde am Code bisher noch nicht wirkliche viele Umbauten nötig machen.


Offtopic:
Meinen Router habe ich jetzt mal bei Ebay rein gesetzt. Mal schaun was da so rauskommt.
Weiterhin habe ich mich mal mit dem Programm Flock vertraut gemacht. Das basiert auf der Firefox Engine und ist perfekt geeignet, wenn man Dinge bloggen möchte, wenn man Feeds vernüftig lesen will (und interessanten daraus bloggen will) und um Bilder von FlickR oder Videos von Youtube sehen möchte (und interessanten daraus bloggen will ^^). Weil irgendwie stehe ich mit dem Editor hier noch auf Kriegsfuß. Das Programm ist recht gut, funktioniert auch tadellos mit meinem – privaten – WordPress-basiertem Blog, hat allerdings so seine Probleme mit MovableType wie es scheint. Ich bilde mir zwar ein, dass die RPC Schnittstelle richtig ist, aber laufen wills dennoch nicht. Vielleicht nutzt das ja jemand auch und hat es geschafft, Flock zu überreden, auf dieser Blog-Software auch tätig zu werden :-).

[PP] ProblemPost

Soooo da das ja bei den Benutzern schon so wunderbar klappt, hier dann mal meine Probleme:
Wie in einem der letzten Posts erwähnt, möchte ich gerne eine Abhängigkeit meiner Entries untereinander haben. Das heißt, ich möchte unter einem Eintrag n Untereinträge haben. Das Klassendiagramm schaut dann so aus:

Meine Tabelle sieht so aus:

Ja eigentlich recht simpel. Nun bietet JPA im Rahmen von Annotation genau eine solche OneToMany Beziehung an.

@OneToMany(cascade=CascadeType.REMOVE)
private List _child_entries = new Vector();

In dieser Liste würden dann alle Kinder des Eintrages stehen.
Da ich mal einfach annehme, dass es unnötig ist, auf DB Ebene noch Foreign Keys zu erstellen, hoffe ich einfach mal, dass das so laufen wird.
Weiterhin habe ich eine Relations Tabelle, die mir die Benutzter mit den Einträgen verbindet und auch sicherstellt, dass n Benutzer für einen Eintrag zuständig sein können:

Hier beginnt es nun problematisch zu werden.
Okay es gibt in dem EJB Buch eine Beschreibung wie man ManyToMany Beziehungen modelliert, allerdings habe ich hier ja auch ein Attribut “status”, welches ja an sich nichts mit der Relation zu tun hat, allerdings schon logisch von dieser Abhängt.
Es würde sich meiner Meinung nach anbieten, dieses als seperates Objekt zu machen.
Ich habe mir das erstmal durch Netbeans generieren lassen. Es ist nun zugegebenermaßen etwas sperrig (weil Netbeans eine eigene PrimaryKey Class drum generieren will, damit sichergestellt ist, dass bei Neuerstellung alle notwendigen Schlüssel belegt sind), allerdings denke ich, dass es eine hinreichende Funktionalität bietet, um erst einmal weiter zu arbeiten.
Kommen wir nun zum Highlight :-/.
Es geht darum, dass die JDBC Anbindung von MySQL seltsamerweise Schwierigkeiten mit Daten zu haben scheint. Siehe dazu auch MySQL Bug #19274.
Ich werde jetzt mal versuchen, eine aktuelle Version zu installieren, die das Problem nicht mehr haben soll. Es geht halt nur darum, dass MySQL mit leeren Datumsangaben (ala 0000-00-00 00:00:00) nichts anfangen kann und eine exception wirft. Das ist in sofern ja nicht schlimm, als das ich einfach einen Eintrag erstellt habe und dann mal hoffe, dass ich damit den Fehler vermeide.
Ich fand weiterhin eine interessante Übersicht, wie JDBC die DB Attribute nach Java überführt:


aus Sun Java System Application Server 9.1 Developer’s Guide

Zu guter Letzte suche ich eine Möglichkeit, um einen klassischen HTTP Redirect (Status 303/301 usw.) Mit der Jersey Umgebung hin bekomme. Es gibt dazu sogar eine Diskussion in dem Blog von Angry Bill unter JSR 311, JAX-RS: Comment and suggestions. Ich fände eine Lösung mit einer @Location Annotation sehr gut. Bisher fand ich nur folgende Lösung, welche ich aber bisher noch nicht ausgiebig testen konnte:
Philosophical content negotiation aus dem Blog von Paul Sandoz (da steht jede Menge zu REST und JSR311 drin)
Abschließend noch eine Bemerkung zu der letzten Diskussion über die Queries.
Im REST-Test von Sun werden u.a. die Pagination-Settings an die URL angehängt also gibt es dann in etwas folgendes als Aufruf:

Request: GET http://localhost:8080/cotodo/resources/users/start=0&max=10&timestamp=1196991596495

Irgendwie schaut das schon wesentlich unübersichtlicher aus. Aber letztendlich muss ich mich eh noch damit befassen, wie ich die Parameter aus der URI extrahiere. Da ich nicht annehme, dass sich die beiden Möglichkeiten großartig unterscheiden werden, kann man sich ja direkt gleich mit der richtigen Lösung befassen.
Ein Problem was ich sehe, ist allerdings die Eindeutigkeit der Parameter, weil diese ja bei steigender Anzahl einen größeren Teil der URI bilden dürften.
Letztendlich ist es aber das gleich Problem wie bei allen Methodenaufrufen, bei denen man anhand eines Aufrufes auch nicht ohne weiteres sagen kann, wofür welcher Wert nun wirklich steht.

Resource User

Ich bin mittlerweile soweit, dass mir ein
Request: GET http://localhost:8080/cotodo/resources/users
eine Liste ala

<users>
    <user>3</user>
    <user>52</user>
    <user>53</user>
</user>

zurück gibt.
Das gleiche ist auch als JSON Format möglich.
Ein Post von
Test31testtest
wird mit einem status 200 (okay) quittiert.
Der erstellte User sieht dann so aus:

<user>
    <name>Test31</name>
    <hash>5a671c66aefea124cc08b76ea6d30bb</hash>
    <id>54</id>
</user>

(Die ID wird automatisch generiert, das Passwort wird im User Objekt per md5 gehashed)
Soweit die guten Nachrichten… alle meine aktuellen Probleme kommen gleich 🙁

kurzes Statement

Jetzt wo es an die Listen geht:
ist so etwas REST konform?

http://localhost/users?q=sonne

Immerhin müsste ein Listenabruf ja per GET erfolgen, also kann man die query nicht per POST übermitteln.


Was anderes am Rande:
ich bin in der seltsamen Situation, einen Draytek Vigor 2930 VS hier zu haben und ihn nicht zu brauchen…. Was der alles kann (außer Wlan – das was ich eigentlich brauche) steht auf der Produktseite bei Draytek.. Ich wollte mal fragen ob den jemand haben will. Im Moment liegt der Preis bei so 300 EUR. Wenn nicht, dann stelle ich den einfach bei Ebay rein :-).