Philipps Blog


2010/01/28  creating JNI with Swig

Filed under:Build,Java,Tooling — philipp @ 12:45 am

I am currently playing around with JNI and Java due the colleagues question to make the connect features of jack-audio (http://jackaudio.org) accessible to java.
There is already a javalib (http://jjack.berlios.de) with some features, there seems still some needes ones missing.
So i started today to have a look into SWIG (http://swig.org).
“SWIG is a software development tool that connects programs written in C and C++ with a variety of high-level programming languages.”
After some hours of research i ended up with some facts:
To created yourself a Java binding to a given c/c++ Program or Library you need one or more Interface files (*.I) and swig file with all the necessary swig module descriptions.
There is an example on the swig homepage ( http://www.swig.org/Doc1.3/SWIGDocumentation.html#Introduction) to explain the workflow of SWIG.
There is a c file exmple.c:

/* File : example.c */
double  My_variable  = 3.0;

/* Compute factorial of n */
int  fact(int n) {
    if (n <= 1)
        return 1;
    else
        return n*fact(n-1);
}

/* Compute n mod m */
int my_mod(int n, int m) {
    return(n % m);
}

The mapping example.i files looks as the following:

/* File : example.i */
%module example
%{
/* Put headers and other declarations here */
    extern double My_variable;
    extern int    fact(int);
    extern int    my_mod(int n, int m);
%}
extern double My_variable;
extern int    fact(int);
extern int    my_mod(int n, int m);

As you can see, the Interface file has a similar syntax with some additional meta information.
You can now create your JNI bindings:

swig -java example.i
There are also flags for different other languages:
-allegrocl - Generate ALLEGROCL wrappers
-chicken - Generate CHICKEN wrappers
-clisp - Generate CLISP wrappers
-cffi - Generate CFFI wrappers
-csharp - Generate C# wrappers
-guile - Generate Guile wrappers
-java - Generate Java wrappers
-lua - Generate Lua wrappers
-modula3 - Generate Modula 3 wrappers
-mzscheme - Generate Mzscheme wrappers
-ocaml - Generate Ocaml wrappers
-octave - Generate Octave wrappers
-perl - Generate Perl wrappers
-php - Generate PHP wrappers
-pike - Generate Pike wrappers
-python - Generate Python wrappers
-r - Generate R (aka GNU S) wrappers
-ruby - Generate Ruby wrappers
-sexp - Generate Lisp S-Expressions wrappers
-tcl - Generate Tcl wrappers
-uffi - Generate Common Lisp / UFFI wrappers
-xml - Generate XML wrappers

As a result you get three new files:

  • example.java
  • exampleJNI.java
  • example_wrap.c

The example_wrap.c can be used to compile the needed library file for your JNI access.
The two java Files are the basic JNI implementation:

    class exampleJNI {
        public final static native void My_variable_set(double jarg1);
        public final static native double My_variable_get();
        public final static native int fact(int jarg1);
        public final static native int my_mod(int jarg1, int jarg2);
    }

And a basic java example how to access these functions:

public class example {
    public static void setMy_variable(double value) {
        exampleJNI.My_variable_set(value);
    }
    public static double getMy_variable() {
        return exampleJNI.My_variable_get();
    }
    public static int fact(int arg0) {
        return exampleJNI.fact(arg0);
    }
    public static int my_mod(int n, int m) {
        return exampleJNI.my_mod(n, m);
    }
}

To get into working with SWIG i can advise the sources of the G4Java Project.
There is also a maven plugin to use SWIG from within your maven build: http://java.freehep.org/freehep-swig-plugin.
I am currently trying to create the necessary Interface files from the jack-audio sources to use them for a first run of SWIG. For python and tck you can use cmake to create these files.

2009/03/28  GnuPG Java Wrapper API

Filed under:Java,Tooling — philipp @ 7:25 pm

Yaniv Yemini wrote a small GnuPG Java Wrapper API.
Just had a small look over it.
So to get it your version from here
Here is just a small demo:

import javax.swing.JOptionPane;
import org.gpg.java.GnuPG;
public class Loader {
    public static void main (String args[]){
        GnuPG pgp = new GnuPG ();
        String toolChain[] = {"sign", "clearsign", "signAndEncrypt", "encrypt", "decrypt"};
        String message = JOptionPane.showInputDialog(null,
                                "Message you want to encrypt?",
                                "Enter your message",
                                JOptionPane.QUESTION_MESSAGE);
        String keyID = "0x56B69D6B";
        System.out.println("using message: "+message);
        System.out.println("using key ID: "+keyID);
        for(String tool : toolChain){
            System.out.println("running: "+tool);
            if(tool.equals("sign")){
                String passPhrase = enterPassPhrase(tool);
                pgp.sign (message, passPhrase);
            }
            if(tool.equals("clearsign")){
                String passPhrase = enterPassPhrase(tool);
                pgp.clearSign (message, passPhrase);
            }
            if(tool.equals("signAndEncrypt")){
                String passPhrase = enterPassPhrase(tool);
                pgp.signAndEncrypt (message, keyID, passPhrase);
            }
            if(tool.equals("encrypt")){
                pgp.encrypt (message, keyID);
            }
            if(tool.equals("decrypt")){
                String passPhrase = enterPassPhrase(tool);
                pgp.decrypt (message, passPhrase);
            }
            System.out.println("result: " + pgp.getGpg_result() + "nn");
            System.out.println("error: " + pgp.getGpg_err() + "nn");
            System.out.println("exit: " + pgp.getGpg_exitCode() + "nn");
        }
    }
    public static String enterPassPhrase(String usage){
        return JOptionPane.showInputDialog(null,
                                "Please enter the Passphrase of your private Key for "+usage,
                                "Passphrase",
                                JOptionPane.QUESTION_MESSAGE);
    }
}

Unforntunetally there is a Problem with decrypting a message. It is possible to decrypt the String with the gpg CI Version, but within Java it does not work. So maybe the error is on my site :-) .

  SortedProperties

Filed under:Java — philipp @ 6:32 pm

Angenommen, man braucht für ein Java Property Set ein geordnete Ausgabe – zum Beispiel um einem Übersetzer eine sortierte Liste mit zu übersetzenden String zu liefern.
Man erstellt eine Klasse (zum Beispiel SortedProperties) und lässt diese von Properties erben.
Bedingt durch die Kapselung ist es notwendig, dass die Methoden
private static char toHex(int nibble) ;
private String saveConvert(String theString, boolean escapeSpace);
private static void writeln(BufferedWriter bw, String s);
und Attribute
private static final char[] hexDigit;
in die neue Klasse kopiert werden müssen.
Wir überschreiben die Methode

public synchronized void store(OutputStream out, String comments)

Diese Methode ist für das eigentliche Speichern in eine Datei verantwortlich.
Der neue Inhalt entspricht bist auf eine zusätzliche Sortierung dem alten:

public synchronized void store(OutputStream out, String comments) throws IOException {
    TreeMap propTree = new TreeMap();
    for (Enumeration e = keys(); e.hasMoreElements();) {
        String key = (String) e.nextElement();
        String value = (String) get(key);
        key = saveConvert(key, true);
        value = saveConvert(value, false);
        propTree.put(key, value);
    }
    BufferedWriter awriter;
    awriter = new BufferedWriter(new OutputStreamWriter(out, "8859_1"));
    if (comments != null)
        writeln(awriter, "#" + comments);
        writeln(awriter, "#" + new Date().toString());
        Set keys = propTree.keySet();
        for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
            String key = (String) iterator.next();
            writeln(awriter, key + "=" + propTree.get(key));
        }
        awriter.flush();
    }

Dies ist tatsächlich ein sehr einfacher Weg, um vorhandene Java-Methoden für eigene Zwecke anzupassen.

2009/02/17  instant jruby & derby enviroment für eine RoR Anwendung

Filed under:Bash,Build,Java,ruby,Ruby on Rails,Tooling — philipp @ 12:04 pm

Als angestammter Java-Entwickler geht es mir oftmals schwer von der Hand, einer Ruby on Rails (RoR) Anwendung mit relativ wenig Auffand eine brauchbare Laufzeit-Umgebung zu bieten.
Normalerweise sollte das OS (MacOS 10.5.6) alles Brauchbare bieten. So ist oftmals ein Rails installiert und auch das (standardmäßig genutzte) SQlite 3 ist vorhanden.
Dennoch sind es oftmals Plugins (spezielle Rails Version / spezielle gems), welche einen zusätzlichen Aufwand benötigen.
Nicht zu vergessen, dass RoR nicht auf allen Systemen vorinstalliert ist und dementsprechend ein interessierter Entwicklung von einem Out-of-the-Box Erlebnis weit entfernt ist.
Sehen wir den Tatsachen ins Auge… die Wahrscheinlichkeit eine installierte JVM vorzufinden ist (noch?) deutlich höher, als eine Lauffähige Ruby-Installation.
Was liegt also näher, als die benötigte Umgebung auf Java fußen zu lassen.
Hierzu werden verwendet:

  • jRuby in Version 1.1.5 (http://jruby.codehaus.org)
  • Derby-DB in Version 10.4.2.0 (http://db.apache.org/derby)
  • weiterhin wird eine installierte JVM (>1.5) vorrausgesetzt

 

Alles weitere wird mit Hilfe von shell-Scripten bewerkstelligt. Wobei momentan nur Unix-Scripte benutzt werden. Eine Portierung auf Windows sollte aber nur eine Sache von Minuten sein.
Es liegt eine RoR-Anwendung in einem Entwicklungs-Status vor. Diese wurde bisher in einem Netbeans-Enviroment mit einer SQlite-DB betrieben.
Das Verzeichnis ist folgendermaßen aufgebaut:

ROOT
|
|- microblog (dies ist unsere RoR-Anwendung)
|
|- derby (derby-installtion - es werden jeweils das bin und lib Verzeichnis benötigt)
| |-bin
| |-lib
|
|- jruby (jruby-installtion - es werden jeweils das bin und lib Verzeichnis benötigt)
| |-bin
| |-lib

Das Hauptproblem besteht darin, dass alle benötigten gems in das entsprechende Unterverzeichnis installiert werden müssen.
Weiterhin muss die Derby-DB mit dem entsprechenden Rake-Task auf mit der aktuellen Schema-Datei instanziiert werden.
Zuletzt sollen die vorhandenen User-Daten in die Derby-DB eingefügt werden.

  1. Anpassen der database.yml
    Wir nutzen weiterhin eine jdbc-Connection. Allerdings ändert sich der Treiber auf den der Derby-DB:
    database.yml

    development:
    adapter: jdbc
    driver: org.apache.derby.jdbc.ClientDriver
    url: jdbc:derby://localhost/microblog_development;create=true
    encoding: utf8
    pool: 5
    username: microblog
    password: microblog
    host: localhost
  2. Export der alten DB-Daten:
    Wir benutzen hierzu das Tool sqlitebrowser (http://sqlitebrowser.sourceforge.net) und erzeugen uns so einen SQL-Dump der alten SQLite-DB. Wir benutzen hierbei nur die SQL-Inserts für den User-Import. Diese speichern wir in die Datei:
    microblog/db/microblog.sql
  3. Für den Import erstellen wir einen Rake-Task:
    microblog/lib/tasks/sql-import.rake

    namespace :microblog do
    desc 'Import old SQL Data'
    task :sqlimport => :environment do
    dbConn = ActiveRecord::Base.establish_connection :development
    sql = File.open("db/microblog.sql").read
    sql.split(';').each do |sql_statement|
    dbConn.connection.execute(sql_statement)
    end
    puts "imported user data '#{Time.now}' "
    end
    end
  4. Erstellen des Setup-Scriptes:
    Folgende Schritte sind notwendig:

    1. Setzen aller benötiger Verzeichnisse
    2. installieren aller benötigter gems
    3. Starten des Derby-DB-Servers
    4. Rake db:migrate
    5. import der alten Daten
    6. Beenden des Derby-DB-Servers

    Das Script sieht wie folgt aus:
    jruby-setup.sh

    #!/bin/sh
    BASE_DIR=`pwd`
    CP=".:$BASE_DIR/jruby/lib/*:$BASE_DIR/derby/lib/derbyclient.jar"
    JAVA_OPTS="-Djdbc.drivers=org.apache.derby.jdbc.EmbeddedDriver"
    JRUBY="$BASE_DIR/jruby/bin/jruby"
    DERBY_HOME=`cd derby && pwd`
    export DERBY_HOME=$DERBY_HOME
    cd $BASE_DIR
    echo "setting up jgems..."
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/jgem update --system
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/jgem install jruby-openssl --no-rdoc --no-ri
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/jgem install -v=2.2.2 rails --no-rdoc --no-ri
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/jgem install activerecord-jdbc-adapter activerecord-jdbcderby-adapter --no-rdoc --no-ri
    echo "starting derby..."
    $BASE_DIR/derby/bin/startNetworkServer &
    echo "setting up derby..."
    cd microblog
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/rake db:migrate
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/rake microblog:sqlimport
    cd $BASE_DIR
    echo "stopping derby..."
    $BASE_DIR/derby/bin/stopNetworkServer &

    Es ist zu erwähnen, dass es notwendig ist, jeweils auf die entsprechende jRuby-Installation zu verweisen.
    Weiterhin benötigt jRuby den entsprechenden derbyClientDriver, welcher in die (von jRuby später verwendete) JAVA_OPTS-Variabel eingetragen wird.
    Ebenfalls musst der Classpath soweit angepasst werden, dass sowohl jRuby, als auch Derby über die notwendigen Bibliotheken verfügen.
    Als letztes ist noch erwähnenswert, dass die beiden Rake-Tasks jeweils aus dem App-Verzeichnis ausgeführt werden.

  5. Das Start-Script.
    Letzendlich sind auch zum eigentlichen Betrieb des Servers Anpassungen notwendig, da auch hier die jRuby-Instanz mit den verwendeten gems benutzt werden sollen.
    Das Script sieht wie folgt aus:
    run.sh

    #!/bin/sh
    BASE_DIR=`pwd`
    CP=".:$BASE_DIR/jruby/lib/*:$BASE_DIR/derby/lib/derbyclient.jar"
    JAVA_OPTS="-Djdbc.drivers=org.apache.derby.jdbc.EmbeddedDriver"
    JRUBY="$BASE_DIR/jruby/bin/jruby"
    export BASE_DIR=$BASE_DIR
    export JRUBY=$JRUBY
    DERBY_HOME=`cd derby && pwd`
    export DERBY_HOME=$DERBY_HOME
    cd $BASE_DIR
    echo "starting derby..."
    $BASE_DIR/derby/bin/startNetworkServer &
    echo "setting up derby..."
    cd microblog
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/rake db:migrate
    echo "starting microblog"
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/microblog/script/server
    echo "stopping derby..."
    $BASE_DIR/derby/bin/stopNetworkServer &

    Es entspricht also einer abgespeckten Variante des Setup-Scriptes. Hierbei wird auch immer ein db:migrate aufgerufen, für den Fall, dass sich die DB-Struktur in der Zwischenzeit geändert haben sollte.

  6. Auslieferung ;-) .
    Derby und jRuby belegen knapp 80 MB sodass es notwendig ist, die Dateigröße für den Transport zu verringern.
    Zuallererst sollten die benötigten gems am besten immer online bezogen werden, sodass man hier ein paar MB sparen kann.
    Weiterhin benutzen wir Jar um die vorhandenen Dateien auf ein 13 MB-Archiv zu packen.
    Die veränderten Scripte sehen wie folgt aus:
    Zuerst das Script, welches die vorhandenen Dateien packt:
    pack.sh

    #!/bin/sh
    find . -name '*.DS_Store' -type f -delete
    jar -cvf statusQ-runtime.jar derby/ jruby/ run.sh pack.sh microblog/db/microblog.sql microblog/lib/tasks/sql-import.rake
    rm -R jruby
    rm -R derby
    rm run.sh
    rm microblog/db/microblog.sql
    rm microblog/lib/tasks/sql-import.rake
    rm pack.sh

    Und nun das geänderte jruby-setup.sh Script, welches vor dem eigentlichen Setup noch für das Entpacken aller Dateien verantwortlich ist:
    jruby-setup.sh

    #!/bin/sh
    jar -xvf statusQ-runtime.jar
    rm -R META-INF
    chmod +x run.sh
    chmod +x setup.sh
    chmod +x pack.sh
    chmod +x jruby/bin/jruby
    chmod +x derby/bin/startNetworkServer
    chmod +x derby/bin/stopNetworkServer
    BASE_DIR=`pwd`
    CP=".:$BASE_DIR/jruby/lib/*:$BASE_DIR/derby/lib/derbyclient.jar"
    JAVA_OPTS="-Djdbc.drivers=org.apache.derby.jdbc.EmbeddedDriver"
    JRUBY="$BASE_DIR/jruby/bin/jruby"
    DERBY_HOME=`cd derby && pwd`
    export DERBY_HOME=$DERBY_HOME
    cd $BASE_DIR
    echo "setting up jgems..."
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/jgem update --system
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/jgem install jruby-openssl --no-rdoc --no-ri
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/jgem install -v=2.2.2 rails --no-rdoc --no-ri
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/jgem install activerecord-jdbc-adapter activerecord-jdbcderby-adapter --no-rdoc --no-ri
    echo "starting derby..."
    $BASE_DIR/derby/bin/startNetworkServer &
    echo "setting up derby..."
    cd microblog
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/rake db:migrate
    $BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/rake microblog:sqlimport
    cd $BASE_DIR
    echo "stopping derby..."
    $BASE_DIR/derby/bin/stopNetworkServer &

Als nächstes sollte die Scripte auf Windows portiert werden.
Weiterhin wäre es interessant, die Derby/jRuby Binaries jeweils direkt online zu beziehen.

2008/07/01  Multicast – genauer Nachgeschaut

Filed under:Java,Network — philipp @ 2:24 am

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

2008/02/07  SSL erzwingen in einem Java Application Server

Filed under:Java,Network — philipp @ 3:33 am

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 :-)

2008/01/28  Upgrade – Downgrade – Löschen – Upgrade: Läuft

Filed under:Build,Java,Tooling — philipp @ 4:05 pm

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.

2008/01/19  1. GUI und die Sicherheit

Filed under:Java,Javascript,UI — philipp @ 5:32 pm

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.

2007/12/17  Sicherheit über JAAS mit einer MySQL Datenbank

Filed under:Architecture(s),Java,Personal — philipp @ 1:44 am

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)

2007/12/13  Abbilden der Relationen

Filed under:DB,Java,Personal,Tooling — philipp @ 12:27 am

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.

« Newer PostsOlder Posts »