very first test with ruby camping :-)


I tried The Camping Short, Short Example for the ruby camping framework.

#!ruby
 #!/usr/local/bin/ruby -rubygems
 require 'camping'

 Camping.goes :HomePage

 module HomePage::Controllers

   # The root slash shows the `index' view.
   class Index < R '/'
     def get
       render :index
     end
   end

   # Any other page name gets sent to the view
   # of the same name.
   #
   #   /index -> Views#index
   #   /sample -> Views#sample
   #
   class Page < R '/(\w+)'
     def get(page_name)
       render page_name
     end
   end

 end

 module HomePage::Views

   # If you have a `layout' method like this, it
   # will wrap the HTML in the other methods.  The
   # `self << yield' is where the HTML is inserted.
   def layout
     html do
       title { 'My HomePage' }
       body { self << yield }
     end
   end

   # The `index' view.  Inside your views, you express
   # the HTML in Ruby.  See http://code.whytheluckystiff.net/markaby/.
   def index
     p 'Hi my name is Charles.'
     p 'Here are some links:'
     ul do
      li { a 'Google', :href => 'http://google.com' }
      li { a 'A sample page', :href => '/sample' }
     end
   end

   # The `sample' view.
   def sample
     p 'A sample page'
   end
 end


It seems to be very easy to create the separat pages.

The example above generates one entry page. (the index method in Views) and one additional page (sample method).
As far as i should know it from rails, it uses the same MVC pattern and theres is a similar syntax. Just the Views are in native ruby without mixing up with html.
But maybe that will appear later on.
The next step is to see howto connect ruby with some inner sys-functions of nexenta ^^.

Blogged with the Flock Browser

Installing ruby camping


Just a short one:

After succeeding in installing ruby gems, i finally was able to install the ruby micro-framework camping.
So ruby should just be used as a small administration web interface. In my eyes a rails aproach seems to be like breaking a fly on the wheel.

Just easy as this:

  root@sunny:~# gem install camping –source http://code.whytheluckystiff.net
    …
    Installing ri documentation for markaby-0.5…
    Installing ri documentation for camping-1.5.180…
    Installing RDoc documentation for activesupport-1.4.2…
    Installing RDoc documentation for builder-2.1.1…
    Installing RDoc documentation for markaby-0.5…
    Installing RDoc documentation for camping-1.5.180…

Smooth and easy. Ready to rumble ;-).

Blogged with the Flock Browser

Install Gems (Ruby with zlib) on Nexenta (GnuSolaris) 1.01

Next in the row is the installation of the ruby gems package.
So it should be easy… just following the instructions.
So…..

root@sunny:~# wget http://rubyforge.org/frs/download.php/38647/rubygems-1.2.0.zip
root@sunny:~# unzip rubygems-1.2.0.zip
root@sunny:~# cd rubygems-1.2.0
root@sunny:~# ruby setup.rb config
./lib/rubygems/spec_fetcher.rb:1:in `require’: no such file to load — zlib (LoadError)
from ./lib/rubygems/spec_fetcher.rb:1
from ./lib/rubygems/source_index.rb:10:in `require’
from ./lib/rubygems/source_index.rb:10
from ./lib/rubygems.rb:767:in `require’
from ./lib/rubygems.rb:767
from setup.rb:22:in `require’
from setup.rb:22

Welcom to error world ;-).
Okay…. should be easy to solve:

root@sunny:~# apt-get install libzlib-ruby zlib1g-dev

or for 64bit Plattforms:

root@sunny:~# apt-get install libzlib-ruby lib64z1-dev

But still the same Error!
Finally i found something in the Weblog of Lucas Chan.
Hm its about readhat/centos…. okay why not.
Just go to the ruby sources:

root@sunny:~# cd /usr/local/src/ruby-1.8.6-p110/
root@sunny:~# cd ext/zlib
root@sunny:~# ruby ruby extconf.rb –with-zlib-include=/usr/include –with-zlib-lib=/usr/lib
checking for deflateReset() in -lz… yes
checking for zlib.h… yes
checking for kind of operating system… Unix
creating Makefile

root@sunny:~# make
root@sunny:~# make install

and finally:

root@sunny:~# cd /usr/local/src/rubygems-1.2.0/
root@sunny:~# ruby setup.rb config
….
== Thanks

Keep those gems coming!

— Jim & Chad & Eric (for the RubyGems team)

Again: something learned!

Blogged with the Flock Browser

Multicast – genauer Nachgeschaut

Da das ja heute bei der Tafelrunde eher etwas zusammengesucht war, habe ich mich noch mal hingesetzt und mir die Dinge an-/eingelesen.
Ich werde einfach mal versuchen die Fragen, die da aufkamen wiederzugeben und dann mit passenden Texten beantworten:

  1. Was ist Multicast?
    Multicast ist eine Nachrichtenübertragung von einem Punkt zu einer Gruppe von Empfängern (auch Mehrpunktverbindung genannt).
    Daneben gibt es noch weitere Arten von Übertragungen:

    • Unicast: eine Punkt-zu-Punkt-Verbindung (klassische Client<->Server Verbindung)
    • Broadcast- und die Anycast-Übertragung (“ich bin da – wer noch?” – ping an x.x.x.255)
    • Geocast, ein besonderer Multicast, der räumlich begrenzt ist.
  2. Was sind die Vorteile von Multicast gegenüber Unicast?
    Es sind gleichzeitige Nachrichten an mehrere Teilnehmer möglich, ohne dass sich die Bandbreite des Senders verändert (für den Sender ist es als würde man eine Nachricht an einen Empfänger senden).
    Handelt es sich um paketorientierte Datenübertragung, findet die Vervielfältigung der Pakete an jedem Verteiler (Switch, Router) auf der Route statt.
  3. Wie geht das nun genau?
    Multicast ist die übliche Bezeichnung für IP-Multicast, das es ermöglicht, in IP-Netzwerken effizient Daten an viele Empfänger zur gleichen Zeit zu senden. Das passiert mit einer speziellen Multicast-Adresse. In IPv4 ist hierfür der Adress-Bereich 224.0.0.0 bis 239.255.255.255 (Klasse D), in IPv6 jede mit FF00::/8 beginnende Adresse reserviert.
    Bei der Übertragung über Ethernet werden die IPv4- bzw. IPv6-Multicastadressen auf bestimmte Pseudo-MAC-Adressen abgebildet, um bereits durch die Netzwerkkarte eine Filterung nach relevantem Traffic zu ermöglichen.
  4. Okaaaaay…. Wer macht sowas? Ist sowas nützlich?
    Ja ist es. Bekannte Anwendungen sind:

    • Audio- und Videoübertragungen (Protokolle wie RTP)
    • Verwendung beim Clustering und beim Routing nach dem Routing Information Protocol (RIP) Version 2.
    • ist für ein funktionierendes AppleTalk-Netzwerk notwendig.
    • Als Service Location Protocol und Multicast DNS wird als Teilimplementierung von Zeroconf Multicast (Rendezvous – inzwischen Bonjour) betrieben
      • automatische Zuweisung von IP-Adressen ohne DHCP-Server
      • übersetzen von Hostnamen in IP-Adressen ohne DNS-Server
      • automatisches Finden von Diensten im lokalen Netzwerk ohne einen zentralen Directory-Server
    • In Windows wird es im Simple Service Discovery Protocol benutzt
    • Weitere Multicast-Protokolle
      • Internet Relay Chat (IRC) bildet Netzwerke, welche einen einfachen TCP-basierten Multicast-Baum realisieren – wer hätte das gedacht ^^
      • Es wird überlegt in Jabber Multicast nach zurüsten.
  5. Und das geht jetzt auch über das Internet oder wie jetzt?
    Jein… also:
    Multicast-Pakete werden von den meisten Routern im Internet nicht verarbeitet. Deswegen werden multicastfähige Teilnetze über Tunnel zu Multicast Backbones (MBones) verbunden.
    Um Multicast-Pakete zwischen mehreren Netzen zu koordinieren, werden spezielle Multicast-Routing-Protokolle verwendet.
  6. Ahja… sehr schön – kann da nix passieren / durcheinander kommen?
    Es existieren bei der Verwendung bestimmter Adressbereiche einiger Switches Probleme bei der Weiterleitung von Multicastnachrichten.
    Die Adressen von 224.0.0.0 bis 224.255.255.255 sind für Routingprotokolle reserviert und für diese Adressen sendet der Router keine IP-Multicast-Datagramme. Die Adressen von 239.0.0.0 bis 239.255.255.255 sind für scoping reserviert, eine Weiterleitung innerhalb dieses Adressbereichs ist ebenfalls Switch abhängig. Adressen im Bereich 225.x.x.x bis 238.x.x.x sind frei verfügbar.
  7. Nachwort:
    Multicasting wird wieder populär, weil IPTV darauf basiert.
    Für verteilte Chat-Netzwerke wurde mittlerweile allgemein eingesehen, dass sie nicht mittels IP-Multicast realisiert werden können.
    Der Einsatz weiterer Multicast-Protokolle ist daher unumgänglich.  Na da haben wirs!

Schamlos kopiert von http://de.wikipedia.org/wiki/Multicast – teiweise gekürtzt und ein wenig angepasst.
Die Formulierungen sind auf die frühe Stunde zurück zu führen.
Anschließend zur Entspannung noch ein wenig Java – ein einfacher Chat-Server:
Zuerst der “Server” – Der ja eigentlich auch Teil jedes Clients ist:

public class NameServer{
        // the multicast group address sent to new members
        private static final String GROUP_HOST = "228.5.6.7";  
        private static final int PORT = 1234;      // for this server
        private static final int BUFSIZE = 1024;   // max size of a message
        private DatagramSocket serverSock;
   
        // holds the names of the current members of the multicast group
        private ArrayList groupMembers; 
        try {  // try to create a socket for the server
          serverSock = new DatagramSocket(PORT);
        }catch(SocketException se){
            System.out.println(se);
            System.exit(1);
        }
        groupMembers = new ArrayList();
        waitForPackets();
       
        // das ist dann hier wieder die typische Server-While-Schleifen-Methode
        private void waitForPackets(){
            DatagramPacket receivePacket;
            byte data[];
            System.out.println("Ready for client messages");
            try {
              while (true) {
                data = new byte[BUFSIZE];  // set up an empty packet
                receivePacket = new DatagramPacket(data, data.length);
                serverSock.receive( receivePacket );  // wait for a packet
       
                // extract client address, port, message
                InetAddress clientAddr = receivePacket.getAddress();
                int clientPort = receivePacket.getPort();
                String clientMsg = new String( receivePacket.getData(), 0, receivePacket.getLength() ).trim();
                processClient(clientMsg, clientAddr, clientPort);
              }
            }catch(IOException ioe){ 
                System.out.println(ioe); 
            }
        }
        ...
    }

Und hier dann noch mal der “Client”-Anteil:
   

public class MultiChat {
   
      // timeout used when waiting in receive()
      private static final int TIME_OUT = 5000;   // 5 secs
     
      // max size of a message
      private static final int PACKET_SIZE = 1024;
      // NameServer address and port constants
      private static final String SERVER_HOST = "localhost";
      private static final int SERVER_PORT = 1234; 
      /* The multicast port. The multicast group address is
      obtained from the NameServer object. */
      private static final int GROUP_PORT = 5555; 
      // for communication with the NameServer
      private DatagramSocket clientSock;
      private InetAddress serverAddr; 
      // for communication with the multicast group
      private MulticastSocket groupSock;
      private InetAddress groupAddr;  
      public MultiChat(String nm){
         /* Attempt to register name and get multicast group
         address from the NameServer */
         makeClientSock();
         waitForPackets();
      } // end of MultiChat();
     
      private void makeClientSock(){
        try {   // try to create the client's socket
          clientSock = new DatagramSocket();
          clientSock.setSoTimeout(TIME_OUT);  // include a time-out
        }catch( SocketException se ) {
          se.printStackTrace();
          System.exit(1);
        } 
        try {  // NameServer address string --> IP no.
          serverAddr = InetAddress.getByName(SERVER_HOST);
        }catch( UnknownHostException uhe) {
          uhe.printStackTrace();
          System.exit(1);
        }
      }  // end of makeClientSock()
     
      private void waitForPackets(){
        DatagramPacket packet;
        byte data[];
        try {
          while (true) {
           data = new byte[PACKET_SIZE];    // set up an empty packet
            packet = new DatagramPacket(data, data.length);
            groupSock.receive(packet);  // wait for a packet
            processPacket(packet);
          }
        }catch(IOException ioe){ 
            System.out.println(ioe); 
            }
      }  // end of waitForPackets() 
    }

Wie man ziemlich gut erkennen kann, ist es auch nicht wesentlich anders, als wenn man sich direkt Sockets erstellt. Gefunden habe ich das Ganze in dem Buch “Killer Game Programming in Java” – heißt wirklich so – hin und wieder finden sich da wirklich interessante Dinge besprochen (Link: hier!).
So das wars.
Die Tage wollte ich noch mal bissel was zu OSGi schreiben – so denn das schöne Buch ankommt.
Und aus – leider aktuellem Anlass – zu IPMI und wieso es eigentlich schon fast unverschämt ist, dass ein _mitgelieferetes_ Linux-Tool fehlerhaft ist und deswegen vom Hersteller empfohlen wird, extra dafür ein Windows aufzusetzen. Nichts gegen Windows, aber wieso liefert man ein fehlerhaftes Tool dann mit?
P.S.: Schreibt mal wieder 😉

Install Ruby on Nexenta (GnuSolaris) 1.01

If you want to build your own small ruby script for administrating you Nexenta-Box, you normally just have to do a:

root@sunny:~# apt-get install ruby

Unfortunetally this Package seems to be broken:

root@sunny:~# ruby -v
ld.so.1: ruby1.8: fatal: libruby1.8.so.1: open failed: No such file or directory

The needed lib libruby1.8.so.1 is not on its desired place.
So you need to compile yourself a version of ruby:


root@sunny:~# apt-get remove ruby
root@sunny:~# apt-get autoremove
root@sunny:~# apt-get install build-essential

root@sunny:~# cd /tmp
root@sunny:~# wget ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6-p110.tar.gz

root@sunny:~# tar xvfz ruby-1.8.6-p110.tar.gz
root@sunny:~# cd ruby-1.8.6-p110

root@sunny:~# ./configure
root@sunny:~# make
root@sunny:~# make test-all
root@sunny:~# make install
root@sunny:~# make install-doc

/usr/local/bin should be in your path!

root@sunny:~# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/X11

root@sunny:~# make install-doc


root@sunny:~# which ruby
/usr/local/bin/ruby
root@sunny:~# ruby -v
ruby 1.8.6 (2007-09-23 patchlevel 110) [i386-solaris2.11]

DONE!

Blogged with the Flock Browser

Splines mit dem HTML Canvas Tag

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

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 🙂

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