Galileo Computing < openbook >
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.


Java ist auch eine Insel von Christian Ullenboom
Buch: Java ist auch eine Insel (Galileo Computing)
gp Kapitel 23 Java Native Interface (JNI)
gp 23.1 Java Native Interface und Invocation-API
gp 23.2 Die Schritte zur Einbindung einer C-Funktion in ein Java-Programm
gp 23.2.1 Schreiben des Java-Codes
gp 23.2.2 Compilieren des Java-Codes
gp 23.2.3 Erzeugen der Header-Datei
gp 23.2.4 Implementierung der Methode in C
gp 23.2.5 Übersetzen der C-Programme und Erzeugen der dynamischen Bibliothek
gp 23.2.6 Setzen der Umgebungsvariable
gp 23.3 Erweiterung unseres Programms
gp 23.4 Erweiterte JNI-Eigenschaften
gp 23.4.1 Klassendefinitionen
gp 23.4.2 Zugriff auf Attribute


Galileo Computing

23.2 Die Schritte zur Einbindung einer C-Funktion in ein Java-Programmdowntop

Wir wollen in einem kurzen Überblick sehen, wie prinzipiell die Vorgehensweise ist. Dazu werfen wir einen Blick auf die Implementierung einer einfachen Klasse, die lediglich die Länge der Zeichenkette berechnet.


Galileo Computing

23.2.1 Schreiben des Java-Codesdowntop

Zuerst benötigen wir eine Klasse mit einer nativen Funktion. Wir haben gesehen, dass der Modifizierer native dafür nötig ist. Die Funktion besitzt, wie eine abstrakte Klasse, keine Implementierung.

public static native int strlen( String s );

Als Nächstes geben wir der Funktion einen Konstruktor, der die dynamische Bibliothek lädt:

Listing 23.1 StrLen.java

public class StrLen
{
  static {
    System.loadLibrary("strlen");
  }
  public static native int strlen( String s );
  public static void main( String args[] )
  {
    System.out.println( strlen("Markus ist doof.") );
  }
}

Galileo Computing

23.2.2 Compilieren des Java-Codesdowntop

Im zweiten Schritt kann der Java-Code übersetzt werden, doch eine Ausführung würde einen Fehler produzieren.


Beispiel Ein Java-Programm wird ausgeführt, und die dynamische Bibliothek existiert nicht (oder ist nicht im Pfad eingebunden).
java.lang.UnsatisfiedLinkError: no io in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1312)
at java.lang.Runtime.loadLibrary0(Runtime.java:749)
at java.lang.System.loadLibrary(System.java:820)
at StrLen.<clinit>(StrLen.java:4)


Galileo Computing

23.2.3 Erzeugen der Header-Dateidowntop

Die nativen Funktionen haben eine feste Signatur, damit sie die JVM bei einem Funktionsaufruf auch finden können. Diese Methoden können von Hand erstellt werden, was jedoch sehr aufwändig ist. Besser ist es, die Header-Datei mit Hilfe des Programms javah für die zu implementierende Funktion zu erstellen. Das Programm liegt dem Java-SDK bei und muss im Pfad eingetragen sein. Der Aufruf sieht wie folgt aus:

$ javah -jni -o strlen.h StrLen

Mit dem Schalter -o bestimmen wir den Namen der Ausgabedatei, die in diesem Fall strlen.h heißen soll.

An der entstandenen Header-Datei StrLen.h sollten keine Änderungen vorgenommen werden. Werfen wir dennoch einen Blick hinein, damit wir wissen, welche Methode wir implementieren müssen:

Listing 23.2 strlen.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class StrLen */
#ifndef _Included_StrLen
#define _Included_StrLen
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     StrLen
 * Method:    strlen
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_StrLen_strlen
  (JNIEnv *, jclass, jstring);
#ifdef __cplusplus
}
#endif
#endif

Die Methode in C heißt nicht einfach strlen(), sondern davor steht noch das Paket, in dem sich die Methode befindet (hier das Standardpaket) und der Name der Klasse. Andernfalls gäbe es leichte Überschneidungen und Angriffspunkte.

Der Methodenname setzt sich im Wesentlichen aus folgenden Elementen zusammen:

gp Vollständiger Klassenbezeichner
gp Trennung der einzelnen Glieder im Paket nicht durch ».« , sondern durch »_«
gp Methodenbezeichner

Alle primitiven Java-Typen sind auf spezielle Typen in C abgebildet, so steht jint für ein Integer und jstring für einen Pointer auf eine Zeichenkette.


Galileo Computing

23.2.4 Implementierung der Methode in Cdowntop

Im automatisch erzeugten Headerfile lässt sich die Signatur der nativen Methode ablesen, die Basis für die Implementierung ist:

JNIEXPORT    jint    JNICALL    Java_StrLen_strlen( JNIEnv *, jclass, jstring );

Wir erzeugen eine neue Datei StrLen.c. Wir wollen dabei erst einmal eine Methode implementieren, die nur etwas auf dem Bildschirm ausgibt. So können wir testen, ob überhaupt alles zusammen läuft. Anschließend kümmern wir uns um die Zeichenkettenlänge:

#include <jni.h>
#include "StrLen.h"
#include <stdio.h>
JNIEXPORT jint JNICALL Java_StrLen_strlen( JNIEnv *env, jclass clazz, jstring s )
 {
  printf( "Hallo Java-Freunde!\n" );
  return 0;
}

Der erste Parameter der C-Funktion ist die this-Referenz. Zwar kann jede nicht statische Methode in Java automatisch this nutzen, doch die Zielsprache C weiß nichts von Objekten und auch nicht, zu welchem Objekt strlen() gehört. Daher übergibt die JVM diese Referenz an die Plattformimplementierung, und die this-Referenz zeigt auf das StrLen-Objekt.


Galileo Computing

23.2.5 Übersetzen der C-Programme und Erzeugen der dynamischen Bibliothekdowntop

Mit dem Compiler muss nun die dynamische Bibliothek übersetzt werden. Da jeder Compiler andere Aufrufkonventionen hat, führen wir das Beispiel am Compiler von Borland (kurz BCC) durch, da dieser unter http://www.borland.com/bcppbuilder/freecompiler/ frei in der Version 5.5 zum Download bereitsteht.1 Das Archiv trägt den Namen freecommandLinetools.exe, das auch mit einer FTP-Suche zu finden ist. Nach der Installation sollten wir den Pfad zum Compiler setzen. Für unsere Demo halten wir uns bei den Verzeichnissen an die Vorgabe C:\Borland\BCC55. Bevor die Arbeit beginnt, müssen wir nur noch zwei kleine Dateien anlegen, die dem Compiler und Linker die Pfade zeigen. Sie liegen beide im bin-Verzeichnis des Compilers.

Listing 23.3 bcc32.cfg

-w
-I"c:\Borland\Bcc55\include"
-L"c:\Borland\Bcc55\lib;d:\Borland\Bcc55\lib\psdk"

Listing 23.4 ilink32.cfg

-x
-L"c:\Borland\Bcc55\lib;d:\Borland\Bcc55\lib\psdk"

Nach dieser Vorbereitung lässt sich die .dll erzeugen. Um flexibel zu sein, greifen wir zu einer Batch-Datei, die den Compiler aufruft und den Linker zur .dll verpflichtet.

Listing 23.5 make.bat

bcc32 -c -Ic:/jdk1.4/include -Ic:/jdk1.4/include/win32 strlen.cpp
bcc32 -tWD strlen.obj

Wir erkennen in der Angabe der Pfade die beiden Include-Verzeichnisse aus dem SDK - das Verzeichnis ist natürlich nur ein Beispiel und muss der persönlichen Installation angepasst werden.

Die Option zum Übersetzen in eine .dll sieht unter jedem Compiler etwas anders aus. Benutzer des Visua- C++-Compilers schreiben:

cl -I"d:\jdk1.4\include" -I"d:\jdk1.4\include\win32" /LD StrLen.c

Mit dem GCC unter Unix können wir Folgendes verwenden:

gcc -Wall -shared -I /usr/local/java/include \
  -I /usr/local/java/include/genunix strlen.c \
  -o libstrlen.so

Hinweis Die dynamische Bibliothek muss unter Windows die Endung .dll und unter Unix-Systemen die Endung .so tragen. Unter Unix-Welt beginnen die dynamischen Bibliotheken mit dem Präfix lib, so dass sich daraus für eine Datei die Namensgebung libName.so ergibt.


Galileo Computing

23.2.6 Setzen der Umgebungsvariabletoptop

Beim Aufruf muss der JVM mitgeteilt werden, wo sie die dynamisch ladbaren Bibliotheken finden kann. Dazu wertet die Laufzeitumgebung die Umgebungsvariable LD_LIBRARY_PATH aus. Diese muss unter Umständen noch gesetzt werden. Befinden wir uns im selben Verzeichnis, ist das nicht nötig.






1 Es gibt noch weitere freie Compiler, etwa der bekannte GCC >http://gcc.gnu.org/), OpenWatcom >http://www.openwatcom.org/).





Copyright (c) Galileo Press GmbH 2004
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de