Jenkins/Hudson Password Hash Format

Nice to know:

The Build-In Security Realm of Jenkins/Hudson is based on acegisecurity. The Hash is Sha256 based. For a Salt

foo

and a password

bar

, you have to Hash

bar{foo}

, that’s then

77ce9123f864f6749a2b2c99b988089c21d33e39247f7b1276dfad01a112f038

(via hashgenerator.de)

You find the Hashes in <Jenkins-Dir>/users/<username>/config.xml

it is then storred as

 

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<user>
    <fullName>user</fullName>
    <properties>
        <jenkins.security.ApiTokenProperty>
            <apiToken>…</apiToken>
        </jenkins.security.ApiTokenProperty>
        <hudson.model.MyViewsProperty>
            <views>
                <hudson.model.AllView>
                    <owner reference="../../.."/>
                    <name>Alle</name>
                    <filterExecutors>false</filterExecutors>
                    <filterQueue>false</filterQueue>
                    <properties/>
                </hudson.model.AllView>
            </views>
        </hudson.model.MyViewsProperty>
        <hudson.security.HudsonPrivateSecurityRealm_-Details>
            <passwordHash>foo:77ce9123f864f6749a2b2c99b988089c21d33e39247f7b1276dfad01a112f038</passwordHash>
        </hudson.security.HudsonPrivateSecurityRealm_-Details>
        <hudson.tasks.Mailer_-UserProperty>
            <emailAddress>mail@example.com</emailAddress>
        </hudson.tasks.Mailer_-UserProperty>
    </properties>
</user>

 

maven is not ant

Neben vielen berechtigten Kritikpunkten die manch einer dem Build-Tool Maven vorwerfen kann, ist oftmals der falsche Einsatz einer der Hauptgründe für die schlechte Perfomance (d.h. lange Build-Zeiten) die fehlerhafte Nutzung von Maven Features.

Oft sieht man, dass Module untereinander durch relative Pfadangaben verknüpft sind. Dies mag bei Ant-Builds ein gängiges Mittel sein, aber wenn man sich vor Augen hält, dass jeder Modul eines Projektes für sich genommen ausgecheckt und gebaut werden können soll, so ist klar, dass man eine abstractere Art und Weise benötigt, einzelne Module miteinander zu verbinden.

Und auch hierfür eignet sich das Mitte der Dependencies. Zu einem vollständigen (und nützlichem) Maven-Build-System gehört zwangsläufig auch ein (eigenes) funktionierendes Maven-Repository (sei es nun Archiva oder Nexus). So ein Repository dient nicht nur zum Lagern von Artefakten, sondern auch als Dreh- und Angelpunkt der einzelnen Module untereinander.

Nehmen wir an, ein Projekt besitzt folgende Struktur:

Root
  |-Modul A
  |-Modul B
    |-Modul C

Nehmen wir weiterhin an, dass Modul A von C abhängt, weil C zum Beispiel XSD Scripte enthält, die separat von einem Fachler aktualisiert werden, aber für die Generierung von Java-Klassen in A benötigt werden.

Würde es nach Ant-Manier gehen, so wäre folgendes denkbar:

<project>
...
    <build>
        <plugins>
            ...
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>xmlbeans-maven-plugin</artifactId>
                <version>2.3.3</version>
                <executions>
                    <execution>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>xmlbeans</goal>
                        </goals>
                    </execution>
                </executions>
                <inherited>true</inherited>
                <configuration>
                    ...
                    <schemaDirectory>../B/C/xsd</schemaDirectory>
                    <classGenerationDirectory>${project.build.directory}/classes</classGenerationDirectory>
                </configuration>
            </plugin>
            ...
        </plugins>
    </build>
...
</project>

Dies dürfte aber spätestens beim Release Build scheitern, weil hier jedes Modul für das Releas separat innerhalb des eigenen Target-Dirs ausgecheckt wird (spätestens hier kommt es dann mit den relativen Pfaden nicht mehr hin).

Was also tun?
Abhilfe schaft das maven-dependency-plugin, welches dem aktuellen Modul erlaub direkt auf die Daten eines einzelnen Artefacts zugreifen zu können. Damit das Artefact durch das Plugin verarbeitet werden kann, sollte es (logischerweise) auch als Dependency im eingetragen sein. So ist sogar sicher gestellt, dass immer der aktuellste Codestand verwendet wird, denn sollt ein Artefact im Repository neuere sein als das, welches lokal vorgehalten wird, so wird dieses heran gezogen.

Die obige Konfiguration würde sich also wie folgt ändern:

<project>
...
    <dependencies>
        ...
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>C</artifactId>
            <version>${project.version}</version>
        </dependency>
        ...
    </dependencies>
    <build>
        <plugins>
            ...
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.3</version>
                <executions>
                    <execution>
                        <id>src-dependencies</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <!-- use copy-dependencies instead if you don't want to explode the
                                sources -->
                            <goal>unpack</goal>
                        </goals>
                        <configuration>
                            <artifactItems>
                                <artifactItem>
                                    <groupId>com.example</groupId>
                                    <artifactId>C</artifactId>
                                    <version>${project.version}</version>
                                    <classifier>resources</classifier>
                                    <type>zip</type>
                                    <includes>**/*.xsd</includes>
                                    <overWrite>true</overWrite>
                                    <outputDirectory>${project.build.directory}/C</outputDirectory>
                                </artifactItem>
                            </artifactItems>
                        </configuration>
                    </execution>
                </executions>
            </plugin>            
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>xmlbeans-maven-plugin</artifactId>
                <version>2.3.3</version>
                <executions>
                    <execution>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>xmlbeans</goal>
                        </goals>
                    </execution>
                </executions>
                <inherited>true</inherited>
                <configuration>
                    ...
                    <schemaDirectory>${project.build.directory}/C/xsd</schemaDirectory>
                    <classGenerationDirectory>${project.build.directory}/classes</classGenerationDirectory>
                </configuration>
            </plugin>
            ...
        </plugins>
    </build>
...
</project>

Zu beachten ist hier, dass die Reihenfolge der beiden Plugins wichtig ist, weil ansonsten die Code-Generierung nicht auf die entpackten Dateien der Dependency zugreifen kann. Ich gebe zu, es handelt sich hier um ein einfaches Beispiel, aber zumindest der Drang, sich durch relative Pfade innerhalb von Maven-Files zu helfen ist meiner Beobachtung nach recht weit verbreitet :-/.

Upload von Third-Party Artefakten in ein Maven Repository per SCP

Manchmal gibt es den Fall, dass es notwendig ist, eine externe Bibliothek in einem Maven Projekt einzubinden. Um nun im Build Zyklus des Maven-Projektes keinen Bruch zu erhalten bietet es sich an, dass Artefakt manuell als Maven Artefakt im Repository zur Verfügung zu stellen.

Wir benutzen bei consolving.de u.a. ein öffentliches Maven Repository ( maven.javastream.de ) in dem wir einige OSS Artefakte den Entwicklern unter unseren Kunden zur Verfügung stellen. Hierbei handelt es sich nicht um ein vollständiges Repository System wie Archiva oder Nexus, sondern um ein lokales Repositry, welche statisch per Web-Server zur Verfügung stellt wird. Das ganze wird befüllt durch einen Jenkins-CI Server, der im Hintergrund alle Projekte periodisch neu dorthin deployed.

Es sind vier Schritte notwendig damit ein Deployment über SCP möglich ist:

  1. Das Verzeichnis des Repositories muss per scp erreichbar sein – hierzu bietet es sich an, den SSH-Key auf dem Server zu hinterlegen.
  2. Einrichten der notwendigen Credentials unter der ~/.m2/settings.xml
    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <settings>
         <servers>
             <server>
                 <id>maven.javastream.de</id>
                 <username>##user##</username>
                 <privateKey>##user##</privateKey>
             </server>
         </servers>
     </settings>
  3. Vorbereiten des Third-Parts Projektes
    Ein vollständiges Maven-Artefakt besteht aus drei Teilen: 1. das Binary, 2. die Sourcen und 3. die JavaDoc Dateien. Alle drei Teile sollten als JAR gepackt werden.
    Nachfolgend ein Script, welches die drei notwendigen Teile in Archive packt:

    #!/bin/bash
    
    # Direkt aus dem Directory zu packen scheint die einzige Möglichkeit zu sein, dass die einzelnen Dateien direkt im Root des JARs liegen.
    cd src
    jar -c . > ../src.jar
    cd ..
    
    cd doc
    jar -c . > ../doc.jar
    cd .
    
    cd lib
    jar -c . > ../lib.jar
    cd .
  4. Zuletzt werden nun die drei JARs per Maven-Command in das Repository geladen:
    #!/bin/bash
    
    #Binary
    mvn deploy:deploy-file -DgroupId=de.javastream \
      -DartifactId=example \
      -Dversion=1.0 \
      -Dpackaging=jar \
      -Dfile=lib.jar \
      -DrepositoryId=maven.javastream.de \
      -Durl=scp://maven.javastream.de/home/javastream.de/maven
    
    # Javadoc
    mvn deploy:deploy-file -DgroupId=de.javastream \
      -DartifactId=example \
      -Dversion=1.0 \
      -Dclassifier=javadoc \
      -Dpackaging=jar \
      -Dfile=doc.jar \
      -DrepositoryId=maven.javastream.de \
      -Durl=scp://maven.javastream.de/home/javastream.de/maven
    
    # Sourcen
    mvn deploy:deploy-file -DgroupId=de.javastream \
      -DartifactId=example \
      -Dversion=1.0 \
      -Dclassifier=sources \
      -Dpackaging=jar \
      -Dfile=src.jar \
      -DrepositoryId=maven.javastream.de \
      -Durl=scp://maven.javastream.de/home/javastream.de/maven

Startup mit dem Play Framework

Wie einige vielleicht ja schon wissen beschäftige ich mich jetzt schon seit einige Zeit mit dem recht neuen, aber mittlerweile immer bekannterem, Java-basiertem Web-Framework Play!. Anders als bei vielen neueren Frameworks wird nicht durch die Mittel einer dynamischen Sprache, sondern durch die Vereinfachung der bestehenden Sprachmittel von java versucht die Entwicklung einer Web-Anwendung zu beschleunigen und zu vereinfachen. Das bedeuten unter anderem das konsequente Weglassen der bisherigen Package-Struktur. Auf der anderen Seite werden altbewährte Bibliotheken aus der Java-Welt verwendet (z.B. JPA, Hibernate, Lucene, usw.). Da es eine ganze Menge an englischsprachigen Tutorials gibt und ich mir schon länger vorgenommen habe in einem kleinen Tutorial die grundlegenden Züge einer Play!-Anwendung zu beschreiben, wird dieser Text mal wieder in Deutsch sein :-).

Play verwendet – wie fast alle aktuellen Web-Frameworks ein MVC-Pattern,

So sollte es einem Entwickler (egal ob Ruby, Java oder Python), der mit der aktuellen Techniken vertraut ist, relativ leicht fallen, sich mit dem typischen Aufbaue einer Play-Anwendung zurecht zu finden.

Die Installation des Frameworks ist auf der Seite des Projektes gut erklärt. Neben einer aktuellen JVM sind keine weiteren Vorausetzungen zu erfüllen. Wichtig ist nur, dass das “play” Script im aktuellen Pfad erreichbar ist.

Nachfolgend möchte ich eine kleine Bookmark-Verwaltung erstellen.

Mit einem

# play new bookmarks

Wird die Grundstruktur einer Play-Anwendung erstellt.

Folder Structure

Das oberste Verzeichnis enthält alle Modelle, Controller und Views der Anwendung (dazu später mehr). Unter “conf” befinden sich folgende Dateien:

– application.conf: Konfiguration der Anwendung – Art der DB, Caching, Konfiguration der Module
– dependencies.yml: Play unterstützt Module, die verschiedene Funktionen kapseln können. Seit Version 1.2 werden die Abhängigkeiten einer Anwendung durch Einträge in dieser Datei gesetzt.
– messages: Diese Datei wird für die Internationalisierung einer Anwendung benötigt (amerikanische Strings). Um eine neue Sprache hinzu zu fügen legt man eine neue Datei mit dem entsprechenden Ländercode an – z.B. messages.de für deutsch.
– routes: Play ermöglicht es, durch Einträge in dieser Datei, das Routing der Anwendung zu beeinflussen. Es erfolgt ein Mapping von URL auf Controller-Actions (dazu später mehr).

Das lib Verzeichnis kann verwendet werden um Java Libs zu verwenden, die nicht als Modul für Play! zur Verfügung stehen. Alle Jars im libs-Verzeichnis werden automatisch in den Classpath der Anwendung aufgenommen.

Das test Verzeichnis enthält die die Testdateien (Junit-/Selenium-Tests sowie ew. Fixures mit Testdaten).

Nach dem ersten Start der Anwendung werden noch zwei weitere Verzeichnisse erstellt:
Ein tmp-Verzeichnis (welches die kompilierten Klassen, sowie Caching Dateien enthält) und ein
db-Verzeichnis, welches bei der Wahl einer h2 Datenbank, die entsprechende Datenkbankdatei enthält (auch dazu später mehr).

Dies soll für die ersten 5 Minuten genügen :-).

creating JNI with Swig

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.

GnuPG Java Wrapper API

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

sorted white and brown pebbles

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.

instant jruby & derby environment für eine RoR Anwendung

Als angestammter Java-Entwickler geht es mir oftmals schwer von der Hand, einer Ruby on Rails (RoR) Anwendung mit relativ wenig Aufwand eine brauchbare Laufzeitumgebung zu bieten.
Normalerweise sollte das OS (MacOS 10.5.6) alles Brauchbare bieten. So ist oftmals eine Rails-Version 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.

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 😉