Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
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?

Spacer
<< zurück
Java ist auch eine Insel von Christian Ullenboom
Programmieren mit der Java Standard Edition Version 6
Buch: Java ist auch eine Insel

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
Pfeil 26 Java Native Interface (JNI)
Pfeil 26.1 Java Native Interface und Invocation-API
Pfeil 26.2 Einbinden einer C-Funktion in ein Java-Programm
Pfeil 26.2.1 Schreiben des Java-Codes
Pfeil 26.2.2 Compilieren des Java-Programms
Pfeil 26.2.3 Erzeugen der Header-Datei
Pfeil 26.2.4 Implementierung der Methode in C
Pfeil 26.2.5 Übersetzen der C-Programme und Erzeugen der dynamischen Bibliothek
Pfeil 26.2.6 Suchort der dynamischen Bibliothek
Pfeil 26.3 Nativ die Stringlänge ermitteln
Pfeil 26.4 Erweiterte JNI-Eigenschaften
Pfeil 26.4.1 Klassendefinitionen
Pfeil 26.4.2 Zugriff auf Attribute
Pfeil 26.5 Einfache Anbindung von existierenden Bibliotheken
Pfeil 26.5.1 C++ Klassen ansprechen
Pfeil 26.5.2 COM-Schnittstellen anzapfen
Pfeil 26.6 Zum Weiterlesen


Galileo Computing - Zum Seitenanfang

26.2 Einbinden einer C-Funktion in ein Java-Programm Zur nächsten ÜberschriftZur vorigen Überschrift

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 - Zum Seitenanfang

26.2.1 Schreiben des Java-Codes Zur nächsten ÜberschriftZur vorigen Überschrift

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 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") ); 
  } 
}

Galileo Computing - Zum Seitenanfang

26.2.2 Compilieren des Java-Programms Zur nächsten ÜberschriftZur vorigen Überschrift

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"

Galileo Computing - Zum Seitenanfang

26.2.3 Erzeugen der Header-Datei Zur nächsten ÜberschriftZur vorigen Überschrift

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.


Galileo Computing - Zum Seitenanfang

26.2.4 Implementierung der Methode in C Zur nächsten ÜberschriftZur vorigen Überschrift

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.


Galileo Computing - Zum Seitenanfang

26.2.5 Übersetzen der C-Programme und Erzeugen der dynamischen Bibliothek Zur nächsten ÜberschriftZur vorigen Überschrift

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

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 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.


Galileo Computing - Zum Seitenanfang

26.2.6 Suchort der dynamischen Bibliothek topZur vorigen Überschrift

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.






<< zurück



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