Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort
1 Java ist auch eine Sprache
2 Sprachbeschreibung
3 Klassen und Objekte
4 Der Umgang mit Zeichenketten
5 Mathematisches
6 Eigene Klassen schreiben
7 Angewandte Objektorientierung
8 Exceptions
9 Die Funktionsbibliothek
10 Threads und nebenläufige Programmierung
11 Raum und Zeit
12 Datenstrukturen und Algorithmen
13 Dateien und Datenströme
14 Die eXtensible Markup Language (XML)
15 Grafische Oberflächen mit Swing
16 Grafikprogrammierung
17 Netzwerkprogrammierung
18 Verteilte Programmierung mit RMI und Web-Services
19 JavaServer Pages und Servlets
20 Applets
21 Midlets und die Java ME
22 Datenbankmanagement mit JDBC
23 Reflection und Annotationen
24 Logging und Monitoring
25 Sicherheitskonzepte
26 Java Native Interface (JNI)
27 Dienstprogramme für die Java-Umgebung
A Die Begleit-DVD
Stichwort
Download: - ZIP, ca. 12,5 MB
Buch bestellen
Ihre Meinung?
<< zurück | Galileo Computing / <openbook> /Java-Insel | vor >>
Java ist auch eine Insel von Christian Ullenboom Programmieren mit der Java Standard Edition Version 6
Java ist auch eine Insel 7., aktualisierte Auflage geb., mit DVD (November 2007) 1.492 S., 49,90 Euro Galileo Computing ISBN 978-3-8362-1146-8 |
| 26 Java Native Interface (JNI)
|  | 26.1 Java Native Interface und Invocation-API
|  | 26.2 Einbinden einer C-Funktion in ein Java-Programm
|  |  | 26.2.1 Schreiben des Java-Codes
|  |  | 26.2.2 Compilieren des Java-Programms
|  |  | 26.2.3 Erzeugen der Header-Datei
|  |  | 26.2.4 Implementierung der Methode in C
|  |  | 26.2.5 Übersetzen der C-Programme und Erzeugen der dynamischen Bibliothek
|  |  | 26.2.6 Suchort der dynamischen Bibliothek
|  | 26.3 Nativ die Stringlänge ermitteln
|  | 26.4 Erweiterte JNI-Eigenschaften
|  |  | 26.4.1 Klassendefinitionen
|  |  | 26.4.2 Zugriff auf Attribute
|  | 26.5 Einfache Anbindung von existierenden Bibliotheken
|  |  | 26.5.1 C++ Klassen ansprechen
|  |  | 26.5.2 COM-Schnittstellen anzapfen
|  | 26.6 Zum Weiterlesen
## 26.2 Einbinden einer C-Funktion in ein Java-Programm 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. ### 26.2.1 Schreiben des Java-Codes Zunächst benötigen wir eine Klasse mit einer nativen Funktion. Wir haben gesehen, dass dafür der Modifizierer native nötig ist. Die Funktion besitzt – wie eine abstrakte Methode – keine Implementierung.
    public static native int strlen( String s );
Die Funktion soll in eine Klasse mit einem statischen Initialisierungsblock eingebettet werden, der die dynamische Bibliothek lädt: Listing 26.1 com/tutego/jni/StrLen.java
    package com.tutego.jni;

    public class StrLen
    {
      static {
        System.loadLibrary( "strlen" );
      }
      public static native int strlen( String s );
    }
Die Methode loadLibrary() delegiert an Runtime.getRuntime().loadLibrary(), die wir auch hätten nutzen können.
Hinweis Eine dynamische Bibliothek ist an einen Klassenlader gebunden und wird auch entfernt, wenn die Garbage-Collection den Klassenlader entfernt. Dennoch kann dieselbe aktive Bibliothek nur einmal von der JVM geladen werden. Ein zweiter Versuch wird mit einer UnsatisfiedLinkError bestraft.
Eine Beispielklasse soll lediglich die Funktion aufrufen: Listing 26.2 com/tutego/jni/StrLenDemo.java
    package com.tutego.jni;

    public class StrLenDemo
    {
      public static void main( String[] args )
      {
        System.out.println( strlen("2003 UB313") );
      }
    }
### 26.2.2 Compilieren des Java-Programms Im zweiten Schritt kann der Java-Code übersetzt werden, doch würde eine Ausführung einen Fehler produzieren. Existiert die dynamische Bibliothek nicht oder ist sie nicht im Pfad eingebunden, folgt ein Fehler wie der folgende:
    java.lang.UnsatisfiedLinkError: no strlen in java.library.path
      at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1682)
      at java.lang.Runtime.loadLibrary0(Runtime.java:822)
      at java.lang.System.loadLibrary(System.java:992)
      at com.tutego.jni.StrLen.<clinit>(StrLen.java:7)
    Exception in thread "main"
### 26.2.3 Erzeugen der Header-Datei Für die nativen Funktionen auf der Java-Seite gibt es auf der C(++)-Seite entsprechende Implementierungen. Die nativ implementierten Funktionen verfügen über eine bestimmte Signatur, damit die JVM bei einem Aufruf an die tatsächliche Implementierung weiterleiten kann. Die Funktionen in C(++) führt eine Header-Datei auf, die später die Implementierung inkludiert. Die Header-Datei erstellt ein Generator, der aus der Klassendatei die Signatur ausliest und nach einem festen Schema die Funktionen auf der C(++)-Seite benennt. Zum Aufruf des Generators bietet sich ein Ant-Skript an, und auch das JDK bringt mit javah ein Dienstprogramm mit. Mit Ant erstellt der Task <javah> die entsprechende Header-Datei: Listing 26.3 build.xml, Ausschnitt
    <javah classpath="bin" outputFile="strlen.h" verbose="yes">
      <class name="com.tutego.jni.StrLen" />
    </javah>
In diesem Beispiel soll für die Klasse StrLen die Header-Datei strlen.h generiert werden. Soll das Kommandozeilenprogramm javah benutzt werden, so bestimmt der Schalter -o den Namen der Ausgabedatei: $ javah -jni -o strlen.h StrLen 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 26.4 strlen.h
    /* DO NOT EDIT THIS FILE – it is machine generated */
    #include <jni.h>
    /* Header for class com_tutego_jni_StrLen */

    #ifndef _Included_com_tutego_jni_StrLen
    #define _Included_com_tutego_jni_StrLen
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     com_tutego_jni_StrLen
     * Method:    strlen
     * Signature: (Ljava/lang/String;)I
     */
    JNIEXPORT jint JNICALL Java_com_tutego_jni_StrLen_strlen
      (JNIEnv *, jclass, jstring);

    #ifdef __cplusplus
    }
    #endif
    #endif
Die Methode heißt auf der C-Seite nicht einfach strlen(), weil sonst Verwechslungsgefahren mit anderen nativen Methoden anderer Pakete nicht ausgeschlossen sind. Aus diesem Grund enthält der Methodenname auf der C-Seite den Paketnamen und den Namen der Java-Klasse. Dementsprechend setzt sich der Methodenname zusammen aus: einem Präfix »Java«, dem vollständigen Klassenbezeichner, wobei die einzelnen Glieder im Paket durch »_« und nicht durch ».« getrennt sind, und dem 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. ### 26.2.4 Implementierung der Methode in C In der automatisch erzeugten Header-Datei lässt sich die Signatur der nativen Methode ablesen; die Basis für die Implementierung ist:
    JNIEXPORT jint JNICALL Java_com_tutego_jni_StrLen_strlen(
                                        JNIEnv *, jclass, jstring );
Wir erzeugen eine neue Datei strlen.c mit einer Implementierung für Java_com_tutego_ jni_StrLen_strlen(). Dabei soll zunächst etwas auf dem Bildschirm ausgegeben werden; wir wollen damit testen, ob überhaupt alles zusammen läuft. Anschließend kümmern wir uns um die Zeichenkettenlänge. Listing 26.5 strlen.c
    #include <jni.h>
    #include "StrLen.h"
    #include <stdio.h>
    JNIEXPORT jint JNICALL Java_com_tutego_jni_StrLen_strlen(
                                JNIEnv *env, jclass clazz, jstring s )
    {
      printf( "Hallo Java-Freunde!\n" );
      return 0;
    }
Der erste Parameter der C-Funktion bekommt so etwas wie eine this-Referenz. Zwar kann jede nicht statische Methode in Java automatisch this nutzen, doch weiß die Zielsprache C 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. ### 26.2.5 Übersetzen der C-Programme und Erzeugen der dynamischen Bibliothek Der C(++)-Compiler muss nun die dynamische Bibliothek übersetzen. Die dynamisch ladbaren Bibliotheken sind unter Windows die .dll-Dateien (dynamic link libraries ) und unter Unix Dateien mit der Endung .so (shared objects). Die .dll- und .so-Dateien erzeugt prinzipiell jeder Compiler, wobei zu beachten ist, dass jeder Compiler andere Aufrufkonventionen befolgt. Auf dem Markt gibt es eine Reihe guter und freier Compiler, die für die Übersetzung verwendet werden können: GCC (GNU Compiler Collection) http://gcc.gnu.org/: Freier Klassiker für unzählige Plattformen Microsoft Visual C++ Toolkit 2005 Express Edition http://msdn.microsoft.com/vstudio/express/visualC/default.aspx/: Übersetzer für C(++)- und .NET-Programme Borland C++ Compiler http://www.borland.com/products/downloads/download_cbuilder.html: Der BCC ist nur für Windows verfügbar. #### Die GNU Compiler Collection Da jeder Compiler andere Aufrufkonventionen hat, führen wir das Beispiel am GCC durch. GCC ist klassischerweise ein Unix-Compiler, doch gibt es ihn auch für Windows. Cygwin ist eine Sammlung von unter Unix bekannten Tools für Windows, und ein Teil davon ist der C(++)-Compiler. Für die Installation wird zunächst unter http://www.cygwin.com/ das kleine Programm setup.exe geladen (es steckt hinter dem Link Install Cygwin now). Im Dialog Choose A Download Source wählen wir Install from Internet, um aus dem Internet alle nötigen Pakete geladen zu bekommen. Im Dialog Select Root Install Directory geben wir zum Beispiel c:\cygwin an. Cygwin speichert die geladenen Teile erst zwischen und möchte dazu auch ein Verzeichnis bekommen – das Verzeichnis kann dann gelöscht werden. Wir geben anschließend eine Download Site an – zum Beispiel aus Deutschland – und wählen anschließend im nächsten Dialog die zu ladenden Pakete. Uns interessiert aus dem Zweig Devel der gcc-core (bzw. gcc-g++). Die Wahl selektiert automatisch ein paar weitere abgeleitete Pakete. (Den gdb können wir angeben, wenn C-Programme debuggt werden sollen.) Nach der Installation können wir eine Konsole aufmachen und in das Verzeichnis C:\cygwin\bin wechseln. Der Aufruf von gcc ohne Argumente liefert bei erfolgreicher Installation die Fehlermeldung »gcc: no input files«. Wir könnten nun den Suchpfad für den gcc-Compiler anpassen, doch soll ein Ant-Skript die Übersetzung steuern. #### Übersetzen mit Ant Das Build-Tool Ant bringt in der Standard-Distribution keinen Task mit, der einen C-Compiler anstößt. Nichtsdestotrotz lassen sich mit <exec> externe Programme aufrufen. Somit sieht das ganze Build-Skript folgendermaßen aus: Listing 26.6 build.xml
    <project default="cc" basedir=".">
      <target name="javah">
        <javah classpath="bin" outputFile="strlen.h" verbose="yes">
          <class name="com.tutego.jni.StrLen" />
        </javah>
      </target>
      <target name="cc" depends="javah">
        <exec dir="c:/cygwin/bin/" executable="c:/cygwin/bin/gcc">
          <arg value="-mno-cygwin" />
          <arg value="-I" />
          <arg value="C:/Programme/Java/jdk1.6.0/include" />
          <arg value="-I" />
          <arg value="C:/Programme/Java/jdk1.6.0/include/win32" />
          <arg value="-shared" />
          <arg value="-Wl,--add-stdcall-alias" />
          <arg value="-o" />
          <arg value="${basedir}/strlen.dll" />
          <arg value="${basedir}/strlen.c" />
        </exec>
      </target>
    </project>
Hinweis Die dynamische Bibliothek muss unter Windows die Endung .dll und unter Unix-Systemen die Endung .so haben. In der Unix-Welt beginnen die dynamischen Bibliotheken mit dem Präfix lib, sodass sich daraus für eine Datei die Namensgebung libName.so ergibt.
Wer den <exec>-Task nicht verwenden mag, der kann auch die externen CC-Tasks unter http://ant-contrib.sourceforge.net/ nutzen. ### 26.2.6 Suchort der dynamischen Bibliothek Um bei der Aufforderung mit loadLibrary() die dynamische Bibliotheken zu finden, 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. Welche Pfade die Laufzeitumgebung durchsucht, zeigt die folgende einfache Zeile:
    System.out.println( System.getProperty("java.library.path") );
Eine Alternative zu loadLibrary(String) ist die Methode load(String). Im Gegensatz zu loadLibrary() erwartet load() einen absoluten Pfad zur Bibliothek. ### Ihr Kommentar Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.
Name |
E-Mail |
Ihr Kommentar |
<< zurück | <top> | vor >>

Copyright © Galileo Press 2008
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, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de