Integration von Java in C++ Anwendungen

Werbung
Integration von Java in C++
Anwendungen
Matthias Müller – 19.11.2015 DOAG Jahreskonferenz 2015 Nürnberg
Die Datenbank-Spezialisten.
Zahlen und Fakten
Robotron Datenbank-Software GmbH
Gründungsjahr
1990
Geschäftsform
GmbH (9 Gesellschafter)
Mitarbeiterzahl
365 (Stand 10/2015)
Stammkapital
2,4 Mio. EUR
Umsatz 2014
31,2 Mio. EUR
Umsatz 2015
32,8 Mio. EUR
Hauptsitz
Oracle-Partner
ISO 9001 zertifiziert
Die Datenbank-Spezialisten.
Schulungs- und Kongresszentrum
Das Leistungsangebot von Robotron

methodische und technologische Kompetenzen

umfangreiches Fachwissen in den branchentypischen Prozessen
Die Datenbank-Spezialisten.
Agenda

Warum Java in C++?

Java Native Interface – Konzepte und Grundlagen

Integration von Java in C++ an einer Beispielanwendung

Probleme und Risiken

Best Practices

Fazit
Die Datenbank-Spezialisten.
Warum?
Die Datenbank-Spezialisten.
Warum Java in C++-Anwendungen?

Erweiterung bestehender Anwendungen um neue Funktionalitäten

„weiche“ Migration von C++-Anwendungen nach Java

Integration mit neueren Java-Anwendungen

Kostenersparnisse

Umgehung von Entwicklungsengpässen
Die Datenbank-Spezialisten.
Konzepte und Grundlagen
Die Datenbank-Spezialisten.
Java Native Interface - Einordnung

Standardisierte und herstellerunabhängige Schnittstelle der JVM

Verbindet C/C++ und Java zwei Richtungen

Die Nutzung von JNI erfolgt über eine dynamische Bibliothek

Plattformunabhängig und Binärkompatibel

Überwindet die Mängel anderer Ansätze (Netscape, Microsoft)
Die Datenbank-Spezialisten.
Java Native Interface – Zentrale Konzepte

Invocation und Interface API

Interface Pointer

Datentypen und Objekt-Referenzen

Objekt-Zugriff

Fehlerbehandlung
Die Datenbank-Spezialisten.
Java Native Interface – Das API

Java Invocation API
– Einbindung und Verwaltung einer JVM in nativen Code

Java Native Interface API
– Verwaltung und Zugriff auf Objekte der JVM
Die Datenbank-Spezialisten.
Java Native Interface – Interface Pointer
Die Datenbank-Spezialisten.
Java Native Interface – Datentypen

Einfache Datentypen
– Direkte Abbildung auf ihre korrespondierenden C/C++ Datentypen
– call by value-Semantik

Komplexe Datentypen und Klassen
– Verwendung von Referenzen
Die Datenbank-Spezialisten.
Java Native Interface – Referenzen

Globale Referenzen
– Threadübergreifend gültig

Schwache globale Referenzen (weak global references)
– Werden vom GC verwaltet

Lokale Referenzen
– Nur im erzeugenden Thread gültig
– Konvertierung in globale Referenzen
Die Datenbank-Spezialisten.
Java Native Interface – Objektzugriff

Erfolgt indirekt über eine Menge an Zugriffsfunktionen
– Ermittlung des Java-Class Objekts der Klasse
– Beschaffung der Methoden-Id
– Aufruf der Methode

Ausnahme sind Arrays
Die Datenbank-Spezialisten.
Java Native Interface – Fehlerbehandlung

JNI führt in vielen Fällen keine Fehlerprüfungen aus

Ausnahmen führen nicht direkt zu einer Unterbrechung

Eine Prüfung auf Ausnahmen erfolgt über
– Den Rückgabewert einer JNI-Funktion
– Dedizierte JNI-Funktionen (ExceptionOccured() oder ExceptionCheck())

Ausnahmen müssen explizit entfernt werden (ExceptionClear())

Bei pending exceptions ist die Ausführung der meisten JNI-Funktionen nicht sicher
Die Datenbank-Spezialisten.
Integration in C++
Die Datenbank-Spezialisten.
Integration in C++ am Beispiel

Generierung eines PDF-Formulars mittels XSL-FO
– Überführung eines XML-Formulars in eine PDF-Datei
– Java-Klassen implementieren den Transformationsprozess
– Ausführung der Transformation durch Apache FOP
– Signatur: int XML2PDF::transform(String xmlFile) throw Exception()

Voraussetzungen
– Java SE Development Kit
– C++-Compiler

Java 8 HotSpot VM
Die Datenbank-Spezialisten.
Beteiligte Komponenten
jvm.dll
C++
Quellen
CC
.exe
jni.h
jvm.lib
Die Datenbank-Spezialisten.
Java
Binaries
Einbindung der Java-VM
// JNI-Repräsentation der JVM
jni::JavaVM * pJavaVM;
// JNI-Struktur für Java-Zugriff in VM für den "Main Thread"
jni::JNIEnv * pJNIEnv;
// VM Optionen festlegen
jni::JavaVMInitArgs vm_args;
jni::JavaVMOption options[nMaxVMOptions];
options[0].optionString = const_cast<char *>(CLASS_PATH);
vm_args.nOptions = 1;
...
vm_args.options = options;
// VM Laden
jni::jint res = jni::JNI_CreateJavaVM(&pJavaVM, (void**)&pJNIEnv,
&vm_args);
Die Datenbank-Spezialisten.
Erzeugung des Formular-Generators
// Klassen-Objekt laden
jni::jclass clsXPdf = pJNIEnv->FindClass("jni/demo/XML2PDF");
if (clsXPdf == 0)
throw std::exception("JNI Fehler");
// Konstruktor beschaffen ...
jni::jmethodID jmiXPdf = pJNIEnv->GetMethodID(clsXPdf, "<init>",
"()V");
if (jmiXPdf == 0)
throw std::exception("JNI Fehler");
// ... und Instanz von XML2PDF erzeugen
jni::jobject jXPdf = pJNIEnv->NewObject(clsXPdf, jmiXPdf);
if (jXPdf == 0)
throw std::exception("JNI Fehler");
Die Datenbank-Spezialisten.
Aufruf der Transformationsmethode
// JNI Methoden-ID für Transformationsmethode beschaffen
jni::jmethodID jmiTransform =
pJNIEnv->GetMethodID(clsXPdf, "transform", "(Ljava/lang/String;)V");
if (jmiTransform == 0)
throw std::exception("JNI Fehler");
// Parameter-String erzeugen
jni::jstring jstrParam = pJNIEnv->NewStringUTF(XML_FILE);
if (jstrParam == 0)
throw std::exception("JNI Fehler");
// Transformation starten
pJNIEnv->CallVoidMethod(jXPdf, jmiTransform, jstrParam);
// Test auf pending Exceptions
if (pJNIEnv->ExceptionCheck() == JNI_TRUE)
throw std::exception("JNI Fehler");
Die Datenbank-Spezialisten.
Fehlerbehandlung
catch (const std::exception & e) {
jni::jthrowable jexc = pJNIEnv->ExceptionOccurred();
if (jexc) {
// Stacktrace auf stderr
pJNIEnv->ExceptionDescribe();
// Alle pending Exceptions aufräumen
pJNIEnv->ExceptionClear();
std::cerr << e.what() << ": " << What(pJNIEnv, jexc) <<
std::endl;
// Lokale Referenz zu Exception freigeben
pJNIEnv->DeleteLocalRef(jexc);
}
}
Die Datenbank-Spezialisten.
Ressourcenfreigabe
// JNI Objektreferenzen freigeben
if (clsXPdf != NULL)
pJNIEnv->DeleteLocalRef(clsXPdf);
if (jXPdf != NULL)
pJNIEnv->DeleteLocalRef(jXPdf);
if (jstrParam != NULL)
pJNIEnv->DeleteLocalRef(jstrParam);
Die Datenbank-Spezialisten.
Probleme und Risiken
Die Datenbank-Spezialisten.
Speicherbedarf der JVM

Zuteilung ausreichenden Speichers bei Start der VM

Keine dynamische Reallokation möglich

Die Java VM kann in einem Prozess nicht neu gestartet werden

Konsequenz ist ein Neustart der Anwendung, um die Speicherzuteilung zu ändern
Die Datenbank-Spezialisten.
Fehlerbehandlung

JNI prüft nicht auf Korrektheit der Argumente

Unbehandelte (pending) Ausnahmen führen zu subtilen Fehlern im Programmablauf

Fehler können zum Absturz der VM führen und damit einen Neustart der Anwendung
notwendig machen

Fehleranalyse ist teilweise aufwendiger
Die Datenbank-Spezialisten.
Internationalisierung

Native C/C++ Anwendungen sind oft an den Zeichensatz des Hosts gebunden

Java verwendet intern mit UTF-16 Unicode für die Zeichenkodierung

JNI bietet String-Zugriffsfunktionen für UTF-16 und modifiziertem UTF-8
– UTF-16 Strings sind nicht NULL-Terminiert
– Natives UTF-8 kann nicht as is an Java übergeben werden

Für andere Kodierungen/Zeichensätze müssen eigene Funktionen bereitgestellt werden
Die Datenbank-Spezialisten.
Eingeschränkte Compileranalyse

Verletzung der Sichtbarkeitsdefinitionen einer Klasse
– Der Aufruf einer Methode mit eingeschränkter Sichtbarkeit führt zu einem Laufzeitfehler
(IllegalAccessException)

Statische Typsicherheit kann nicht gewährleistet werden
– Verletzungen führen zu Laufzeitfehlern

JNI verwendet variable Parameterlisten in Methodenaufrufen
– Inkorrekte Behandlung der Argumentlisten führt teilweise zu schwer analysierbaren LaufzeitProblemen
Die Datenbank-Spezialisten.
Weitere Stolperfallen

Verletzung der Thread-Korrektheit
– JNI Environment zum Aufruf von JNI-Funktionen wird im falschen Thread verwendet

Fehlerhafte Ressourcenfreigabe
– Referenzen werden nicht wieder freigegeben
– Benutzung ungültiger Referenzen

Performance-Probleme bei komplexen Datentypen (Arrays) in der Schnittstelle
Die Datenbank-Spezialisten.
Best Practices
Die Datenbank-Spezialisten.
Korrektheit und Robustheit I

Fehlerprüfungen sind langweilig – aber WICHTIG

Kapselung der JNI-Strukturen und Funktionen
– Verwendung von Wrapper-Klassen
– Minimale Schnittstelle zu JNI-Funktionen
– Zentralisierung der Fehlerbehandlung
– Ressourcenfreigabe durch Dekonstruktor
Die Datenbank-Spezialisten.
Korrektheit und Robustheit II

Erstellung von Peer-Klassen für Java-Objekte
– Transparente Integration in C++-Code
– Codereduzierung
– Wartbarkeit

Mapping der Java-Ausnahmen in C++-Ausnahmen

Vernünftige Limitierung des Heap Space der Java VM
Die Datenbank-Spezialisten.
Performance

Caching von häufig benutzten JNI-Strukturen
– Attribut- und Methoden-Id
– Objekt-Referenzen
– JNI Environment JNIEnv des Threads

Sorgfältiges Design der Schnittstelle C++/Java
– Vermeidung iterativer Zugriffe auf Java-Arrays
– Häufige Kontextwechsel zwischen C++ und Java vermeiden

Rückgabewerte testen anstatt auf Ausnahmen
Die Datenbank-Spezialisten.
Fehleranalyse

Verwendung der Java-Option –Xcheck:jni

Schreiben von Log-Dateien in Java-Komponenten

Generierung von Debug-Information durch javac

Die Option –verbose:jni protokolliert die JNI-Aufrufe
Die Datenbank-Spezialisten.
Fazit
Die Datenbank-Spezialisten.
Fazit

Zuverlässige, plattformunabhängige und portable Schnittstelle

Mit einem kleinen Satz an Basisklassen gestaltet sich auch die Programmierung einfach

JNI-basierte Lösungen im Projekt auf über 3000 PC bisher ohne Probleme

Java Versionswechsel der Clients ohne neues Rollout der Anwendung
Die Datenbank-Spezialisten.
Matthias Müller
Senior Systemberater
Robotron Datenbank-Software GmbH
Stuttgarter Str. 29 | D-01189 Dresden
Tel. +49 (0) 351-25859 - 0
[email protected]
www.robotron.de
Fragen?
Die Datenbank-Spezialisten.
Herunterladen