Monday, August 27, 2012

Home Server Setup - The Second

Last year i bought a sweet small cube that should be my new home server, running with linux, to do all the stuff I want to do at home. This post will be about the second setup of this server. The first installation was my first "Linux server experience". As I was a little bit frightened to fail on the command line I installed a desktop edition of ubuntu. A friend who introduced me into the Linux world always said: "A server with a GUI actually isn't a real server!". Of course! But I felt good with that and now as I got some experience with Linux on my server I am sure that I will make the whole "command line server thing" - so I'm using the Ubuntu Server edition.

The original reason for this post is the setup of the server relating to sharing media files with serveral users in the home network. As I had to discover there were still some problems on which I had to spend much time to solve them. So to give it a name: I wan't to tell about Samba and further file administration problems.

Before I can start with the linux specific stuff, I will explain the environment and the conditions in my home network. There are actually only two users: me and another one. I have, as one can imagine, a Linux machine and he (the other one) has a Windows machine. Although it doesn't effect the problem directly I have to mention that the other user uses a very well known smartphone of Apple and therefore also iTunes. But this is another problem. The server runs with Linux, too. Both client machines shall have access to shared directories on the server, for instance for:
  • music
  • videos
  • pictures
  • documents
  • backups.

That is to say, we both use the same media directories with different programmes from different platforms.

Now to the server side. The media data to share is stored on a RAID 1 array mounted at /media/data owned by the root user. The first steps to take are about creating the directories you wan't to share.

sudo mkdir /media/data/music
sudo mkdir /media/data/videos
... (also for the other directories)

The next step is to set up the users and groups that will be allowed to access shared media data. In this setup I will create the users user1 and user2, and the group share_media. Every user in this group shall get access to the data.

sudo groupadd share_media (create the group)

sudo adduser user1
sudo usermod -a -G share_media user1
... repeat the procedure for user2

At the moment the directory /media/data still is owned by the user root and the group root. To let the subdirectories be accessed by users of the created group, the directories have to be assigned the group share_media.

sudo chgrp -R share_media /media/data/*

This assigns the group to all children of /media/data. In my case the directory lost+found is also a child which should be owned by the root user. In this case you should replace * by the special sub directory name.
Now the time is right for installing Samba, a server that enables Linux to share files or directories with other Windows, MAC OS or Linux machines.

sudo apt-get install samba

For configuring Samba you have to open the configuration file /etc/samba/smb.conf in your favourite editor. I do it with vim. How vim works you can see here linuxconfig.org/Vim_Tutorial. Because the owner is root you have to prepend sudo again.

sudo vim /etc/samba/smb.conf

Typically many debian (the ancestor system of Ubuntu) configuration files consist of
  • comments marked by the sign "#"
  • sections surrounded by square brackets, example: [section name]
  • settings in the form of: key = value
Then you append your declaration for your shared directories at the end of this file. You may give some comments before that. You do this analogously for the other shared directories.

[Shared Music]
comment = A shared directory for music on the server.
path = /media/data/music
writeable = yes
browseable = yes
guest ok = no
valid users = @share_media
create mask = 0775
directory mask = 0775

Actually the last two lines were the most difficult thing costing me much time to figure out that I had to set these lines. The explanation for this comes a bit later when I talk about file permissions. Although we already have set unix passwords for our users we still must set up users and passwords for Samba. You do it like the following line shows.

sudo smbpasswd -a user1 (then type in the password for user1 again)
... repeat it for user2

Now you can restart the Samba server to get the changes applied.

sudo service smbd restart

When you try to access the shared directory from another machine in the network you should be promted to type in your user name and password. Actually without the mentioned second lines in the Samba share configuration you would only be able to read the shared directories. So now I will explain what these lines are about.

The typical umask (see en.wikipedia.org/wiki/Umask for understanding the term) is 0022 (or shorter 022). This means when directories are created the default permissions are rwxr-xr-x. So the owner (first 3 signs) is able to read, write and execute (when it is a directory the x means you can access it). Users in the same group (second 3 signs)  and other users (last 3 signs) are only allowed to read and to execute.
For the shared directory this would mean: When user1 creates a sub directory, user2 is not allowed to write in that directory (create files). This is not the way that I need it. So I had to define an other mask for Samba. The mask system used in smb.conf is another one than umask. There you still have 4 positions with numbers but each number is the sum of the 3 granted rights:
  1. read with number 4
  2. write with number 2
  3. execute / access with number 1.
So e.g. 7 means 4 + 2 + 1 (everything allowed) and 5 means 4 + 1 (read and execute). The line (0)775 means then analogous in the symbolic representation of permissions rwxrwxr-x.

Until now you have declared how Samba should handle the share and modification operations (rwx). But the shared directories still have the default permission values. These have to be set now.

sudo chmod -R g+rws /media/data/* (take care for lost+found explained above)

That's it. Now you should be able to create, modifiy and delete directories and files with different users from different systems.

Monday, January 9, 2012

Android und Maven

In einem aktuellen Projekt habe ich mit Webservices und Android zu tun. Wenn ich bisher mit Java EE zu tun hatte, habe ich Maven als Projektverwaltungstool benutzt. Nun möchte ich auch das Android-Projekt von Maven verwalten lassen. Im Netz habe ich unter folgendem Link einen extra Archetype für solche Maven-Projekte gefunden.
https://github.com/akquinet/android-archetypes/wiki/android-quickstart-archetype
Dort ist ein ganz einfaches Beispiel beschrieben, wie ein solches Maven-Projekt aufgesetzt wird. Dies sieht wie folgt aus:
 mvn archetype:generate
  -DarchetypeArtifactId=android-quickstart
  -DarchetypeGroupId=de.akquinet.android.archetypes
  -DarchetypeVersion=1.0.7
  -DgroupId=your.company
  -DartifactId=my-android-application 
Dies funktionierte ohne Probleme. Danach kopierte ich die Dateien eines bestehenden Android-Projekts in das durch Maven generierte Projekt. Die danach aufgetretenen Fallstricke möchte ich in diesem Post kurz erklären.
  1. Das ist zwar ein Standardfehler, bei meinen Android Projekten, aber er trat wieder auf. Wenn wieder mal Eclipse neu eingerichtet wird und dann das Android-Eclipse-Plugin für Android installiert wird, sollte man nicht vergessen den Installationspfad zum SDK zu setzen.
  2. Ebenso ist der Pfad in der Maven-Konfiguration des Android-Build-Plugins zu setzen.
  3. Das Build-Plugin fordert ebenso eine minimale Maven-Version, die ich ebenso nachinstallieren musste. Dies ist die Version 3.0.3.
  4. Im Android-Manifest sollte man die benötigte Android-API-Version (android:minSdkVersion)noch prüfen.

Wednesday, October 26, 2011

Android: Der Vortrag in der Java User Group Görlitz

Hallo an alle Android-Interessierten!

in diesem Post möchte ich die Folien und das Eclipse-Android-Projekt vom Java-User-Group-Vortrag zum Download zur Verfügung stellen. Bei dem Projekt handelt es sich um eine ganz primitive Memo-App. Wer noch Fragen hat, ist dazu angehalten diese zu stellen.


Hier die zwei Links:
Weiterhin gibt es noch die gratis Version des Buches "Android - Grundlagen und Programmierung" (Arno Becker und Marcus Pant) unter folgendem Link zum Download:
Beim Durcharbeiten ist bitte zu beachten, dass an einigen Stellen Fehler enthalten sind, bzw. einige Erläuterungen fehlen. Dazu sind auf der Website zum Buch unter der Rubrik Errata Korrekturen und Anmerkungen zu lesen, die dann hilfreich sind, wenn irgendetwas im Buch nicht nachvollziehbar ist.

Viel Spaß

    Thursday, October 6, 2011

    Ein paar Gedanken zum Testen von Regeln


    ​Da man mit Hilfe von Regeln fest einprogrammierte Logik aus einem System in diese stets veränderbaren Regeln hinauszieht, muss diese Logik natürlich auch getestet werden. Es steht also die Frage im Raum, wie ich Regeln Testen kann. Insbesondere die Frage nach der Testumgebung ist interessant.
    Fest steht:
    • Die unmittelbare Ausführungsumgebung der Regeln ist die Rule-Engine (Regelmaschine).
    • Eigentlich ist bei Tests darauf zu achten, dass der Ausgangszustand immer derselbe ist. Für Regeln ist dies jedoch nicht sinnvoll. Denn gerade das Zusammenspiel von Regeln und das Wiedereinführen von Entscheidungen der Rule-Engine bilden das reele Verhalten von Rule-Engines und Regeln ab.
    • Entsprechende Fakten werden je nach Testfall in die Regelmaschine eingefügt. Je nachdem, welche Regel erwartungsgemäß aktiviert werden sollte, muss die Faktenbasis präpariert werden.
    Tests per JUnit ausführen zu lassen, wäre möglich. Es ist die Frage, ob dies sinnvoll ist, da nie klar ist, welche Regeln es geben wird, da diese nach Bedarf bzw. nach Anforderung erstellt wird. Sie werden also während der Laufzeit erstellt, erweitert bzw. modifiziert. Der Ausführungszeitpunkt von Unit-Tests ist hingegen vor der Inbetriebnahme des Systems. Daher sollte es eine Komponente geben, die eine Testumgebung bereitstellt, die man nach Bedarf konfigurieren kann. In dieser müsste man dann zur Betriebszeit die Regeln austesten können. Da bei Aktivierung von Regeln eine Konsequenz ausgeführt wird, die auf das Produktivsystem bzw. Umgebungssysteme Auswirkungen hat, müsste man an dieser Stelle eventuelle Modifikationen ermöglichen, die unerwünschte Auswirkungen für den Test zu unterdrücken.

    Für den Test von Regeln interessiert hauptsächlich, ob eine bestimmte Regel aktiviert wird, weniger, ob eine Konsequenz ausgeführt wird. Wenn eine Regel also zur Aktivierung markiert ist (man sagt, sie ist auf der Agenda), dann ist die Ausführung der Konsequenz gewiss. Da die Logik der Konsequenz entkoppelt ist, kann man davon ausgehen, dass diese auch separat getestet worden ist.
    Lediglich für den Fall, dass in einer Konsequenz Modifikationen an der Faktenbasis vorgenommen werden, sollte man diese Ausführung auch zulassen.
    Heißt also, auch die Aktivierung der Regel muss für den Test konfiguriert werden können.

    Thursday, August 18, 2011

    Ein Beispiel-Szenario für Drools-Regeln

    In dem Post "Drools für meine Rules" habe ich auf ein später folgendes Hello-World-Beispiel verwiesen. Nun ist es soweit.

    Das Beispiel-Szenario bezieht sich auf das Projekt des Human Task Service (http://www.humantaskservice.blogspot.com/). Eine Task-Instanz (TI) ist im Kontext dieses Systems das technische Äquivalent zu dem abstrakteren Begriff des Human Tasks. Die TI gehört also zum Domänen-Modell des Human Task Service (HTS). Für dieses Beipiel ist ein vereinfachtes Modell die Grundlage.

    Jede TI wird zum Erzeugungszeitpunkt mit einem Zeitstempel versehen. Weiterhin ergibt sich aus der zugewiesenen Task-Beschreibung die maximale Dauer für die Bearbeitung einer Task-Instanz bzw. den Human Task. Diese muss für in jedem Fall zugesichert werden können, damit der HTS als wirtschaftliche Dienstleistung angeboten werden kann. Neben der Erfüllungsdauer gibt es noch weitere Gütemerkmale von Diensten. Diese werden in sog. Service Level Agreements definiert. Es muss für die Verwendung der Rule-Engine Drools also eine Regel formuliert werden, die den erklärten Sachverhalt erfüllt.

    Hier kommt die Regel dazu:
    <?xml version="1.0" encoding="UTF-8"?>
    <package
        name="de.saxsys.hti.monitoring.controller"
        xmlns="http://drools.org/drools-5.0"
        xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
        xs:schemaLocation="http://drools.org/drools-5.0 drools-4.0.xsd">
        <import name="de.saxsys.hti.monitoring.domain.*"/>
        <import name="de.saxsys.hti.monitoring.util.mail.*"/>
        <import name="de.saxsys.hti.monitoring.util.deadline.*"/>
        <import name="de.saxsys.hti.monitoring.handler.*"/>
        <import name="javax.xml.datatype.*"/>
        <import name="java.math.BigDecimal"/>
        <import name="java.util.*"/>

        <function
            return-type="String"
            name="prepareMessageText">
            <parameter
                identifier="$ti"
                type="TaskInstance"/>
            <body>             
                StringBuffer sb = new StringBuffer();
               
                sb.append("An task instance with state '").append($ti.getState().value());
                sb.append("' has passed the half of the deadline!\n");
                sb.append("--------------------------------------------\n");
                sb.append("Detailed data:\n\n");
                sb.append("id: ").append($ti.getId()).append("\n"); 
                sb.append("name: ").append($ti.getName()).append("\n");
                sb.append("creation: ").append($ti.getCreationTime()).append("\n");
               
                Date creationDate =    $ti.getCreationTime();
                String duration = $ti.getTaskDescription().getServiceLevelAgreement().getDeadline().getTimeSpan();
                sb.append("deadline: ").append(DeadlineChecker.getEndDate(creationDate, duration));
               
                return sb.toString();
            </body>
        </function> 

        <rule name="deadline is half passed">
            <lhs>
                <pattern object-type="TaskInstance" identifier="$ti">
                    <and-constraint-connective>
                        <field-constraint field-name="state">
                            <qualified-identifier-restriction evaluator="!=">State.COMPLETED</qualified-identifier-restriction>
                        </field-constraint>
                        <field-constraint field-name="state">
                            <qualified-identifier-restriction evaluator="!=">State.FAILED</qualified-identifier-restriction>
                        </field-constraint>
                    </and-constraint-connective>
                </pattern>
                <eval>
                    DeadlineChecker.halfOfDeadlineHasPassed($ti.getCreationTime(), $ti.getTaskDescription().getServiceLevelAgreement().getDeadline().getTimeSpan())
                </eval>
            </lhs>
            <rhs>
                $ti.setPriority(Prioritizer.increase($ti.getPriority()));
                System.out.println("... increase priority to: " + $ti.getPriority().value());
                System.out.println("... inform responsible person of half passed deadline");
                String message = prepareMessageText($ti);
                HTIReactionHandler htiRH = (HTIReactionHandler) ReactionHandlerFactory.getReactionHandler(ReactionHandlerType.HTI_HANDLER);
                htiRH.sendMail("department_chief@anycompany.com", "Warning", message, $ti.getAttachment());
            </rhs>
        </rule>
    </package>
    Nun die Erläuterung des Dokuments:

    1. Zuerst das Wurzelelement: <package></package> Mit dem Attribut name wird eine Bezeichnung angegeben, welche die in diesem Dokument enthaltenen Regeln im Speicher der Rule-Engine eindeutig von anderen Regeln unterscheidet.
    2. In den Regeln kann Java-Codeenthalten sein. Die genutzten Klassen werden durch den  import-Tag geladen.
    3. Regeln werden durch das Element <rule/> ausgedrückt. Ein Regel besteht aus einem Bedingungsteil (left hand side) und dem Konsequenzteil (right hand side). Die Tags <lhs/> und <rhs/> besitzen diese Semantik und werden als Kindelemente des rule-Tags eingefügt.
    Der Bedingungsteil:
    1. Eine Bedingung wird in Drools durch sog. Patterns, also Muster, ausgedrückt. Man beschreibt mit Patterns wie entsprechende Datenobjekte ausehen sollen, welche die Bedingung erfüllen sollen. Mit dem Attribut object-type wird der Typ bzw. die Klasse des Objekts festgelegt. Über einen Bezeichner, der mit dem Attribut identifier angegeben wird, kann man in dem Dokument auf das aktuelle Objekt zugreifen.
    2. Bisher würde die Bedingung lauten: "Objekte müssen vom Typ TaskInstance sein, damit der Konsequenzteil (rhs) aktiviert wird.". Darum werden mit Feld-Beschränkungen (Restriktionen) genauere Angaben zu dem Objekt gemacht. Mit dem field-constraint-Tag wird beschrieben, welche Felder das Objekt haben muss.
    3. Als Kindelemente des field-constraint-Tags können zum Beispiel folgende Elemente verwendet werden, um die Werte der Felder zu beschränken:
      • Um Enumerationen anzugeben, wird eine qualified-identifier-restriction verwendet.
      • Für alle anderen (primitiven bzw. standard) Typen kann der folgende XML-Tag benutzt werden: <literal-restriction evaluator=">" value="0"/> Mit solchen Restriktionen können Felder mit Zahlen oder Zeichenketten ausgezeichnet werden. Mit dem evaluator-Attribut wird angegeben, wie der jeweilige Wert des Feldes auzuwerten oder zu interpretieren ist.
    4. Um solche Feld-Restriktionen können nun noch logische Verknüpfungen (<and-constraint-connective/>, <or-restriction-connective/>) geschachtelt werden.
    5. Um die Patterns können weiterhin Negationen (<not/>), der Existenz- (<exists/>) und der All-Quantor (<forall/>) geschachtelt werden.
    6. Neben den Patterns zur Beschreibung der Beschaffenheit eines Objekts gibt es noch die Möglichkeit, diese durch eigenen Java-Code zu prüfen. Dafür wird der Tag <eval></eval> benutzt. In ihm kann beliebige Logik formuliert sein, die weitere Objektmerkmale prüfen kann. Die einzige Einschränkung ist, dass der Ausdruck zu einem Boolschen Wert evaluiert.
    Der Konsequenzteil:
    1. Im Konsequenzteil der Regel kann nun jedweder Java-Code stehen. Entsprechende Importe sind dafür nicht zu vergessen. 
    2. Um auf das aktuelle Objekt zuzugreifen und die Konsequenz für ein solches auszuführen, benötigt man eine Referenz darauf. Diese konnte durch die angabe eines Bezeichners (identifier) im Pattern angelegt werden. Damit haben wir nun Zugriff auf die Daten des Objekts. Für ein besseres Verständnis sorgt es, wenn in den Regelbedingungen angelegte Bezeichner mit beispielsweise dem Dollar-Zeichen den Unterschied zu im Java-Code definierten Bezeichnern singnalisieren.
    3. Typisch für XML ist der Overhead, der durch die Dokumentstruktur entsteht. Aus diesem Grund, und für die Wiederverwendbarkeit ist es sinnvoll, Logik zu kapseln. Einfacherweise macht man dies in Java-Klassen, die nur eingebunden und aufgerufen werden brauchen. Eine zweite Möglichkeit ist, die Verwendung von Dokument-Internen Funktionen. Ein Exemplar davon ist genau unter den Import-Beschreibungen des Beispieldokuments zu finden. Weiterer Erklärung bedarf es nach all den Erläuterungen nicht. Wie zu sehen, kann im Funktionsrumpf wieder Java-Code stehen. Funktionen sind vor allem dafür geeignet, regelspezifische Logik im Regeldokument zu kapseln. Logik, die in unterschiedlichen Regeldokumenten gebraucht wird, sollte besser in Javaklassen verpackt werden.
    Nachdem nun die Syntax des Regeldokuments recht ausgiebig erklärt wurde, folgt nun eine kurze Beschreibung der Semantik. Was soll die Regel tun?

    Für jede Task-Instanz, die noch nicht im Zustand beendet (COMPLETED) oder fehlgeschlagen (FAILED) ist, und deren Bearbeitungsfrist bereits zur Hälfte überschritten wurde, soll eine Konsequenz ausgeführt werden.
    Sinn der Regel ist das Scheitern des Human Tasks zu verhindern. Darum lautet die Konsequenzhandlung: Es muss eine zuständige Person, z.B. ein Abteilungsleiter, per E-Mail über den dringenden Zustand informiert werden.

    Alle Sprachelemente und Funktionen konnten natürlich nicht gezeigt werden, jedoch wurde in diesem Post eine kleine Einführung in Drools-XML-Regeln gegeben.

      Sunday, July 24, 2011

      Android: Eine kleine Radio-App

      Oft hat es mich bereits gefrustet, dass es vom MDR bereits für Jump eine Android-App gab, mit der man den Internetradio-Stream hören konnte, aber für mein geliebtes Figaro dagegen nicht. Einen Livestream haben Sie zwar schon, jedoch ist dieser nur über die Internetseite von MDR Figaro abspielbar.

      Nun ja, das stimmt nicht ganz, man kann auch noch eine Wiedergabeliste dafür herunterladen. Ein beliebiger Player kann den Stream dann abspielen. Nachdem ich mir die Wiedergabeliste, eine pls-Datei, mal im Texteditor angeschaut hatte, war mir klar, dass ich auch selbst was damit anstellen könnte.

      So schaut eine pls-Datei aus:
      
      [playlist]
      numberofentries=1
      File1=http://c22033-l.i.core.cdn.streamfarm.net/22007mdrfigaro/
        live/3087mdr_figaro/live_de_128.mp3 
      Length1=-1
      
      
      Gestern früh kam mir dann der Gedanke: Jetzt wo ich selbst ein Android-Telefon besitzte und ich auch noch Java kann, sollte ich mir doch mal eine eigene Figaro-App schreiben.

      Den entsprechend geeigneten Player zu finden , der von Android für Internet-Audiostreams geboten wird, war nicht schwer. Der AsyncPlayer ist sehr einfach zu benutzen. Man übergibt einen URI und sagt ihm, dass er den Stream abspielen soll. Nach ein paar Sekunden beginnt er. Will man die Wiedergabe anhalten, ruft man die Stop-Methode auf. Mehr kann man mit diesem Player nicht anstellen. Folgender Code zeigt ein Beispiel:
      
      android.media.AsyncPlayer player = new AsyncPlayer("StreamPlayer");
      Uri uri = Uri.parse("http://c22033-l.i.core.cdn.streamfarm.net/22007mdrfigaro/
        live/3087mdr_figaro/live_de_128.mp3");
      player.play(this, uri, true, AudioManager.STREAM_MUSIC);
      // Der Stream läuft
      player.stop();
      // Der stream ist gestoppt. Pause gibt es nicht.
      
      
      Die App kommt aufgrund der Einfachheit mit einer Activity aus. Leider habe ich doch mehr als 5 Stunden daran gesessen. Grund dafür ist die bescheidene Android-Oberflächengestalltung. Das Layout macht nie das, was es soll :-) Die Beschreibung der View in XML ist eine saubere Sache, da Logik und Anzeige getrennt werden. Jedoch wäre es meines Erachtens nach gut gewesen, sich bei der Beschreibung der Oberfäche an XHTML und CSS zu orientieren. Lange Rede, kurzer Sinn: Die entworfene Oberfläche ist sehr einfach gehalten. Ein Play/Pause-Button und ein Stop-Button sowie ein Hintergrundbild habe ich für die View verwendet.

       Wer Interesse an der App hat, kann Sie sich unter folgendem Link herunterladen.

      http://dl.dropbox.com/u/3538883/Downloads_Max_Blog/MDRFigaroWebRadio.apk

      Sunday, July 10, 2011

      Drools für meine Rules :-)

      Schon seit ein paar Tagen habe ich mich mit Drools beschäftigt. Jetzt endlich kann ich sagen welche Rule Engine die jenige ist, die ich fürs Monitoring benutzen kann. Was habe ich also die letzten Tage gemacht?

      Zuerst einmal findet man im Netz relativ viel Material zu Drools. Die Beispiele für Regeldefinitionen haben mich natürlich am meisten interessiert. Schnell hatte ich einige gefunden, die ganz gut aussahen. Daher begann ich mich näher mit Drools zu beschäftigen um ein kleines Hello-World-Projekt aufzusetzen. Damit fingen die Probleme an.

      Nachdem ich alle Ressourcen heruntergeladen  und eingebunden hatte, habe ich ein Rule-File nach den Beispielen im Netz geschrieben. Leider lief nichts. Bei der Fehlersuche verwirrte mich Anfangs, dass es noch so viele andere Sprachen für Drools gab. Das genau ist der Punkt!

      Drools hat erstens sein eigenes Regelformat namens "drl" (kein XML) und zweitens unterstützt es außerdem noch Regeldefinition via XML. Das Tückische daran ist, dass Drools, aktuell in der Version 5.2.0 vorliegend, eine sehr starke Entwicklung durchlaufen hat. So gibt es einige verschiedene XML-Formate, die alle nicht mehr aktuell sind. Die Materialien, die man im Netz dazu findet, sind oft für die alten Formate gewesen. Wenn man das weiß, stellt es kein Problem dar :-)

      Mein Hello-World-Beispiel habe ich zum laufen bekommen. Es ist das Ergebnis dieses Wochenendes. Eine komische Fehlermeldung wird jedoch nach wie vor angezeigt. Trage ich die Schemaversionen analog zum Versionsstand (5) von Drools ein, findet er das Schema nicht. Trage ich die Version 4 ein, wie es in der aktuellen Dokumentation von der Drools-Seite zu lesen ist, bekomme ich die Meldung, dass ich doch bitte mal Version 5 eintragen soll. Seltsam!

      Mein Hello-World-Beispiel kommt später.