Gut! Jetzt habe ich eine erste Variante meines JPile-JNI-Wrappers für den Code pilespace.cpp von Ralf. Ich stelle den Code hier gerne zur Verfügung, möchte aber hinweisen, das ich den Code in der Freizeit nebenher auf die “Schnelle” mit der “heissen Nadel” gestrickt habe. U.a. habe ich alle nur erdenklichen Java-Patterns “missachtet”, so habe ich z.B. die Methodennamen in der Datei PileSpaceJNI.java groß geschrieben, um möglichst nahe an der C/C++-Version von Ralf zu bleiben. Außerdem habe ich kein Factory-Pattern oder einen Singleton implementiert, obwohl ich denke das dieses Pattern sehr sinnvoll wäre. Ebenso habe ich keinerlei Java-Paketierung verwendet. Ich empfehle aber, eine Paketstruktur der Art pile.space.* zu verwenden. Mir ging es in diesem Schritt ausschließlich darum (1) etwas über JNI zu lernen und (2) den PileSpace-Kern in Form einer Win32-DLL für die Java-Welt zugänglich zu machen.

Wie bin ich vorgegangen?

Implementieren der PileSpaceJNI.java

In dieser Klasse werden folgende Native-Methoden aus pilespace.cpp zugänglich:

  1. public native void GetCoordinates(int hSpace, long v, Coordinates coords);
  2. public native int Open();
  3. public native long GetValue(int hSpace, long x, long y);
  4. public native long SetValue(int hSpace, long x, long y, long v);
  5. public native void Initialize(int hSpace, short nPartionBits);
  6. public native void Close(int hSpace);
  7. public native long FindValues(int hSpace, long xy, boolean isXCoordinate, long partitionFrom, long partitionUntil, ValueListItem ppHead)

Die Methode FreeValueListItems() wird dank Java und der GarbageCollection obsolet. Noch nicht implementierte aber exportierte Methoden wie Flush() habe ich ebenfalls nicht beachtet. Wie man sieht, habe ich mich möglichst an die Namen gehalten, die in der Datei pilespace.cpp verwendet werden. Der unsigned int wurde auf einen Java long gemappt. Problematisch waren die Rückgabewerte in C/C++, die als Pointer auf die Strukturen Coordinates und ValueListItem an die Methoden GetCoordinates() und FindValues() übergeben wurden. Aus diesen C-Strukturen (struct) wurden die Klassen Coordinates.java und ValueListItem.java. Diese beiden Javaklassen erhielten folgende Callback-Methoden, die ich aus dem C/C++-Kontext aufrufe, um die Werte an Java zurückzugeben:

Coordinates.setX()
Coordinates.setY()
ValueListItem.add()

Erzeugen der Headerdatei PileSpaceJNI.h

Diese Datei wird von javah aus der kompilierten Klasse PileSpaceJNI erzeugt. Diese Header-Datei ist notwendig, um die C/C++-Methoden an die JVM zu exportieren. Dies geschieht mit dem Aufruf

javah PileSpaceJNI

Implementieren von PileSpaceJNI.cpp

Diese Datei entspricht eigentlich der originalen pilespcace.cpp-Datei von Ralf. Im nächsten Schritt werde ich wahrscheinlich einen “sauberen” Wrapper bauen, indem ich die pilespace.dll von Ralf komplett “unangetastet” lasse und dann einfach zu meiner Wrapper-DLL hinzulinke. Man möge mir also auch bitte diese “Sauerei” verzeihen :-) Interessant sind hier die JNI-Aufrufe aus dem C-Kontext, um die Callbackmethoden aufzurufen.

Die DLL lässt sich mit MinGW problemlos mit folgendem Aufruf kompilieren:

g++ -Wall -D_JNI_IMPLEMENTATION_ -Wl,–kill-at -I/c/apps/dev/Java/jdk1.5.0_01/include -I/c/apps/dev/Java/jdk1.5.0_01/include/win32 -shared PileSpaceJNI.cpp -o PileSpaceJ
NI.dll

Starten und ausprobieren

In der PileSpaceINJ-Main-Methode() habe ich exemplarisch einen PileSpace geöffnet und erhalte das PileSpaceHandle hSpace mit dem ich die Methoden SetValue(), GetValue(), GetCoordinates() und FindValues() aufrufe. Analog dazu gibt es die datei test.cpp die dasselbe mit der originalen pilespace.dll auf purer C-Ebene vollzieht.

Die Java-Implementation ruft man einfach mit

java PileSpaceJNI

auf.

Alle Dateien findet ihr hier: PileSpaceJNI